My Pixel-Art Editor Workflow: Vue, Laravel, and AI
I built Pixanima—a browser-based pixel art and animation tool—and the "secret sauce" for the AI sprites isn't the prompt; it's a PHP GD pipeline that forces the image into a grid. Here is the actual logic:
1. Generate a raw image (I use flux-schnell).
2. Area-downscale it to the target grid (e.g., 32x32).
3. Quantize colors via imagetruecolortopalette with ordered dithering.
4. Run a tolerant corner flood-fill to kill the background and create transparency.
That's how you avoid "AI slop" and get something a developer can actually use in a game engine.
The "Client-Side" Business Logic
The editor is built with Vue 3 and Laravel 12. Since the canvas and project files live in IndexedDB, the whole thing is client-side. Here is a reality check for solo devs: you can't effectively paywall a client-side feature without being a jerk to your users.
Because of this, the editor is free. The only thing I charge for is the AI, because that's the only part with a real marginal cost and a backend requirement.
AI Inbetweening via FILM
The most useful feature I've implemented is AI inbetweening. Instead of drawing every single frame of a walk cycle, you provide two keyframes. I run Google's FILM frame-interpolation model on the PNGs, get an mp4 back, and use ffmpeg to slice it into stills. Each frame then hits the pixelization pipeline mentioned above. It turns a tedious chore into a few seconds of waiting.
Bulletproof Credit System
When you're paying for API calls, you can't have a buggy ledger. I implemented an idempotent, charge-then-generate system. The user's balance is just a cache; the credit_transactions table is the only source of truth.
Here is the simplified logic for the credit deduction to prevent double-charging:
DB::transaction(function () use ($userId, $cost, $reference) {
$user = User::whereKey($userId)->lockForUpdate()->first();
if ($user->credit_balance < $cost) {
throw new InsufficientCreditsException();
}
// Ensure we haven't processed this specific request reference already
if (CreditTransaction::where('reference', $reference)->exists()) {
return;
}
$user->decrement('credit_balance', $cost);
CreditTransaction::create([
'user_id' => $userId,
'amount' => -$cost,
'type' => 'spend',
'reference' => $reference
]);
});This setup ensures that if a request fails or retries, the user isn't robbed of their credits. It's a basic AI workflow pattern, but it saves a massive amount of customer support headaches.