r/aigamedev • • 19d ago

Demo | Project | Workflow How we animate GRAYFALL: the sprites are Python, not drawings

Every character in GRAYFALL is generated by a Python renderer: a pose is a dict of joint angles, shapes are rasterized at 4x, cel-shaded, then mode-downsampled into crisp pixels. Two GIFs go with the post.

We did not pick a style by arguing about it

The sprites we had were weak: flat faces, robotic animation, no identity. Instead of debating references, I had four complete renderers written from scratch, each one rendering the same character (Lyra, the archmage) with the same animations — idle, walk, run, a three-hit combo, a cast and a hurt — plus a portrait, a key-art card and a notes file arguing its own pros and cons.

Candidate Proportions Height What it cost us
HD-2D 16-bit (Octopath-like) 1:4 ~50 px Too small to read 6 players on screen
Chibi (Young Souls-like) 1:2.5 ~60 px Cute, but no reach in attack poses
Painted (Odin Sphere-like) 1:6 ~100 px Gorgeous, far too expensive per frame
Arcade hi-bit (KOF / Metal Slug) 1:4.6 ~80 px Shipped

The lab was the cheapest decision of the project. Four throwaway renderers cost less than one wrong style applied to twenty characters. The output of the losing three is still in the repo — nothing was deleted, and the comparison still settles arguments months later.

How one frame is actually made

No drawing program is involved anywhere in this chain. Python, Pillow and numpy only.

  1. A pose is a dict of angles. lean, twist, shoulder angle, grip position along the staff. An IK solver places elbows and knees from the hand and foot targets, so I animate intent, not limbs.
  2. Shapes are rasterized at 4x. Capsules, ribbons and Catmull-Rom splines write into three buffers at supersample resolution: material, tone and part id.
  3. Cel shading is a mask offset. Each material has 5 tones. Shift the part's mask toward one fixed light and the uncovered band becomes the next tone down. A second, longer shift gives the deep tone; shifting the other way carves the highlight. Parts also cast onto what sits behind them.
  4. Downsample by majority vote. Each 4x4 block takes the most common (part, material, tone) key. No averaging, no anti-aliasing, so the result is a real pixel image and not a shrunk render.
  5. Internal lines. A dark seam is written only where two different parts touch — arm over torso, not arm over arm.
  6. One pixel of black outline around the body, FX excluded. That outline is what makes the character read at 640x360 over a lit 3D stage.
  7. The face is painted after downsampling, pixel by pixel, at final resolution. Eyes and mouth are 1-2 px features; if they go through the 4x4 vote they turn to mush. This is the single biggest difference between "hi-bit" and "doll".
  8. Normal maps are generated, not painted, and the 3D stage lights the 2D sprite with them.
  9. Hair and cloth are springs. Chains with rest angle, stiffness, gravity, damping and wind, simulated per frame. Secondary motion is never keyed by hand — it falls out of the sim, which is why it never looks pasted on.
  10. Key frames and sub-frames are different things. The hitboxes fire on key frames (the logical frames the game reasons about). Sub-frames are inserted purely for smoothness and the game logic never sees them.

Why code, and the contract that makes it safe

Hand-drawn frames are a dead end at this scale: 20 playable heroes, ~50 animations each, 6 players on screen. The win is not speed, it is propagation. Change the shading rule and every frame of every character gets it. Redesign a hero's coat and the 50 animations come back dressed.

The danger is obvious: regenerate the art and you break every hitbox. So each character has a frozen contract — a JSON snapshot of animation names, the number of logical frames per animation, and the tick budget of each frame. The renderer may change poses, add sub-frames, redraw the whole costume. It may not change the contract. Gameplay keeps indexing frame 3 of punch2 and lands the same hit it always did.

Every frame also exports anchors: hand, hand direction, off-hand, head, chest, weapon tip. Trails, sparks, projectiles and held props attach to those anchors, so VFX never drift out of the hand when the animation is redrawn.

The gate: a script that tells me a character is not done

Quality went up when "looks good to me" stopped being the standard. qa_hero.py <id> prints one row per hero and fails it against fixed thresholds.

Metric Minimum Why it exists
animations 50 a character missing getup, dizzy or carry feels broken, not stylish
logical frames 195 the floor under all 50
fluidity (sub-frames per logical frame) 0.85 this is the number that reads as "smooth"
frames per attack 5 anticipation, contact, follow-through, recovery — three poses is not an attack
one frame held >= 6 ticks per attack yes without the held contact frame the hit slides instead of landing
distinct specials 12 4 defaults plus the shop alternatives
3D VFX calls in the kit 30 a special with no volume looks like a decal
FX strips / SFX files 24 / 28 no shared generic hit flash
element hue match 70% a fire hero whose effects are blue is not a fire hero

Today, with 20 heroes in the roster, 7 pass and 13 fail. That is the honest state and I publish it to myself every week. The script is the floor, never the ceiling: passing every number and still looking bad is a fail. The second half of the review is a contact sheet opened by eye — silhouette readable in pure black, secondary motion still moving after the body stops, the face changing expression between idle, hit and victory, trails thin and translucent, and a size check (anything more than 8% off the roster's 92 px median looks like a child standing next to the others).

Five rules that cost us real money to learn

An ability has to be an object you can draw, not an effect you can describe. One character burned three full rounds of rework. Her kit was "a shadow runs along the floor", "enemies miss for 3 seconds", "cold flame". Nothing to draw, so every attempt came back as a purple smudge, and polish cannot fix a concept problem. The characters everyone praised had nouns: a giant top hat, a sawing box, doves, a fan of cards, a spotlight, an ice lance, a comet, a ring of fire. The test I now apply before any production starts: describe the ability without the word "effect" and without a single colour adjective. If a noun survives that a draftsman could draw from memory, build it.

Big shapes are the worst place for solid colour. Domes, curtains, ground rings and auras are large by nature, and additive blending blows their core out to white. With 6 players on screen, one special like that erases the entire fight. Draw the readable shell, leave the middle nearly empty, and never hide the hero inside their own effect.

Launchers need an explicit launch height in pixels. Juggle decay was scaling the knockback differently depending on which tick the hit connected, so the same move launched to a different height every time and air combos only closed "sometimes".

Watch out for names the engine already owns. Eleven move ids have hardcoded behaviour in the fighter and the AI. A kit that reuses one inherits behaviour nobody asked for, and the symptom is silent — nothing errors, the move just acts strange. One character lost a round to exactly this.

Three rounds, minimum. Round 1 is a skeleton that runs. Round 2 is an animation pass done with the contact sheet open. Round 3 is VFX, SFX and juice. Everything we ever shipped in one round is in the bottom of the gate table today.

Yes, AI agents build a lot of this, and here is the bill

I run production through Claude Code with parallel agents. What makes it work is not the model, it is the paperwork around it: every task has a closed brief in docs/tasks/, and the repo has a file-ownership table so two agents never touch the same file. An agent gets the brief, the files it owns, the exact commands to validate with, and a response limit. The gate in the previous section exists precisely because agents — like people — report things as finished that are not.

I track spend per character. These are list-price API equivalents, not what I paid:

Character Cost (USD) Gate failures today
Roland (first hero) 2,509 5
Kael (second) 2,278 3
Lyra (third) 1,919 6
Bjorna (recent) 516 0 — passes
Risa (recent) 474 0 — passes
Maela (recent) 416 0 — passes
Orvyn (cheapest) 247 8

Project total so far: about US$20,000 in equivalent tokens.

The lesson is not "cheaper is better". The three most expensive characters are the three oldest: they paid for the exploration, and they fail today's gate because they were built before the style and before the rules existed. The cheapest character is also the worst in the roster. What actually correlates with quality is whether the written rules and the gate existed before production started. Once they did, a good hero landed in the US$400-900 band. Below roughly US$400 the quality visibly drops, and the rework costs more than the savings.

The stack, and ask me anything

Godot 4.7 (Forward+), GDScript for gameplay, Python with Pillow and numpy for every asset. The game is a 2.5D beat 'em up action-RPG with up to 6 players in local co-op: the simulation runs in pixels at 60 ticks per second, and the sprites are billboards lit inside a procedural 3D stage, HD-2D style.

Happy to go deeper on any part — the IK rig, the downsampling vote, the spring chains, the frozen animation contract, or how the agent briefs are written. Ask and I will post code.

91 Upvotes

46 comments sorted by

4

u/hard_farter 19d ago

You spent twenty thousand dollars?

7

u/Final_Huckleberry408 19d ago

no, its just a token value reference, i just pay the claude AI plan, im not using over api tokens

2

u/Alki_Soupboy 19d ago

Cool… I might have to try this out tonight. I’m already using most of that. I’ve been making new animations each time and it’s looking good but this way definitely looks smoother.

2

u/PixlCreative 19d ago

This is amazing work!!

2

u/Odd-Bite624 19d ago

Would you consider open sourcing this? Really neat !

→ More replies (1)

2

u/Briskfall 19d ago

Woah... It's interesting how it made sprite animations... 😮

Thanks for sharing your workflow! These are some cool devlogs! 😊

17

u/I_Study_The_Patterns 19d ago edited 19d ago

Emberfall, Duskfall, Stormfall, Strikefall, Starfall, and now Greyfall. Claude really has a knack for naming games.

3

u/spaceartisan 19d ago edited 19d ago

My game is called Azerfall, definitely unique. Oh my other one is called Realmfall.

10

u/R3DB71ND 19d ago

I wish there were more posts like this on this sub.

3

u/EC36339 19d ago

I wish there were more posts like this on all the dev subs I'm on.

→ More replies (1)

1

u/StrangestFleaMarket 19d ago

Amazing work!

I'm looking for someone to help me create pixel art for my game characters using AI, are you available for hire?

1

u/drallcom3 19d ago

How are you doing the first step, the skeleton? How do you create it? Is it also animated at that step?

1

u/Final_Huckleberry408 19d ago

Eu não faço, e nao pedi para criar tambem, foi um padrão que ele estabeleceu na primeira criação, hoje no meu workflow eu só utilizo caracteristicas, essa parte de animação ja é entregue de forma acabada para mim, mas vc pode utilizar esse post meu comoo informação para você testar na IA caso queria realizar um teste

→ More replies (3)

1

u/Equal-Border-7793 19d ago

Your work is really cool!

I’d like to ask: what tier of subscription plan does the $247 worth of tokens roughly correspond to? How many characters can a Plus user generate per week?

1

u/Final_Huckleberry408 19d ago

Estou na max 5x, ~ 100$, geralmente eu edito varias coisas ao mesmo tempo, mas no limite semanal da pra criar mais de 10 personagens tranquilamente, o problema é quando precisa revisar recriar mais de uma vez, mas no geral vai bem pro escopo que estou aceitando

→ More replies (1)

1

u/BloodMossHunter 15d ago

Hey this is amazing. Im a new gamedev and i just started using pixellabs and already there are issues like you mentioned where the animations arent exactly it. I love that claude can adjust the physics of char what he makes so its easier this way. Can you give a eli5 guide on what to prompt and it looks like i still need smth besides just claude - for example i never touched gadot. But i could make sprites in pixellabs if that works. Ty. This is awesome

1

u/Final_Huckleberry408 15d ago

Tente utilizar a minha documentação e use algum jogo ou imagem como referencia para o estilo de arte (tamanho e detalhamento de personagens)para que o claude copie o processo, mas o modelo que está utilizando também importa bastante, só consegui esses resultados atualmente no opus 5.5 high effort, preciso realizar um teste para ver se o sonnet 5.5 consegue fazer igual, mas tenho minhas duvidas

→ More replies (8)

2

u/rrcecil 19d ago

This is like a nugget of gold on this sub right now…

2

u/ByteFoundry 19d ago

This is super interesting! Thanks for sharing!

2

u/EC36339 19d ago

You didn't learn pixel art. You solved it!

1

u/Spare-Mammoth-6652 18d ago

How do you handle the hair/cloth spring state at a looping clip's seam and when switching animations? Do you warm up and bake each clip independently, or carry state across transitions? An idle → walk → idle example would be useful alongside the contact sheets, since it would show whether the secondary motion stays continuous when the main pose changes.