# Pixel art from a 3D hero, part 2: held parts, steady heads, and Ragnarok's colours

In the [first post](https://blog.sprited.ai/pixel-art-from-a-3d-hero-no-dithering-eight-ways-compared) I compared eight ways to turn Materia's 3D sailor girl into pixel art without dithering. The best of them (part-aware cel: each block votes for a part, then for one of that part's shades) read as pixel art in a still frame. In motion it still boiled, and none of the eight was good enough to ship.

This post covers the round after that. The sailor is now in the game as a pixel variant, drawn by the same code in the bake and in our studio, where every setting can be tuned live.

![](https://assets.spritopia.com/blog/pixel-sailor-2/walk-8dir.gif)

## 1. Move parts, not pixels

Watch a pixel artist animate a walk. The head bobs one pixel down and back up, and it is the *same* head drawing each time. A renderer that resamples the whole figure every frame redraws the head from scratch whenever it moves a third of a pixel, so its edges crawl.

So each rigid part is drawn on its own grid: the head with its hair, the chest, each half of each arm and leg, and the hips with the skirt. A vertex belongs to whichever bone moves it most. Each part hangs from its root bone, snapped to a pixel corner. A part keeps its last drawing and only moves by whole pixels until its bone turns more than 10° from the camera's view. Then it is drawn again.

![](https://assets.spritopia.com/blog/pixel-sailor-2/stepper-strip.png)

On the walk, the share of the head redrawn per frame went from 26 % to 4 %. The rest is a head stepping a whole pixel, which is what a bob should look like.

## 2. Eyes are drawn, not sampled

At 90 px tall her eyes are 2 to 4 pixels. Sampled from the render they vanish or turn into brown blots, because the iris and the hair's shadow share shades. Pixel artists draw them, so the renderer does too. Each eye's centre lives in the head bone's space and is projected every frame. A small stamp chosen by how far the face is turned (3×4 front, 2×3 three-quarter, 1×3 side) goes where the eye is, if the bangs don't cover it.

![](https://assets.spritopia.com/blog/pixel-sailor-2/eyes-front.png)

The stamps first moved on their own and slipped a pixel against the head. Now they hang from the head's pixel corner and are held with the head's drawing, so they don't flicker.

## 3. Priority: what must survive the shrink

The collar's yellow trim is one 768 px line. In a block vote it always loses to the cloth around it. [Pixelat3D](https://sol.sbc.org.br/index.php/sbgames/article/view/45414) (SBGames 2026) handles this with hand-painted priority maps that pick the most important texel in each block. I took the idea into the vote: costumes can mark a whole mesh (the ribbon) or the texels near a colour (the trim). A marked pixel counts up to three times. A pure "highest priority wins" let the yellow eat the navy collar.

![](https://assets.spritopia.com/blog/pixel-sailor-2/prio-pr-close.png)

## 4. Ragnarok's colours

Jin's reference is Ragnarok Online, so I measured real RO sprites, reading their palettes and renders:

- **Ramps.** Every material is one 8-colour ramp in the palette, of which 3 to 8 show in a frame. The steps run 7 to 12 L apart. Warm colours run from yellow in the light to red in the shadow.
- **Highlight.** The hair's highlight is a separate near-white colour, and only a dot or two of it.
- **Outlines.** These use each material's own darkest shades. Only 0 to 11 % of edge pixels are near-black.
- **Size.** A standing character is about 90 px tall.

Our sailor broke most of these rules. K-means merged her ramps to 2 or 3 shades (the skirt had 2), and a 32-colour palette over the whole hero merged them again. Her lines were near-black, and her hair highlight was a broad band.

Now every material gets 4 shades, split by lightness quantiles of its own pixels, so even an evenly lit skirt uses all 4. The face gets 8. The hair's highlight is the brightest few pixels, thinned to one pixel per clump and at most two per piece of hair. The outline is the part's own darkest shade, one step darker on the silhouette. A thin line also goes where an arm crosses the body, found from the bones the skin follows. She is 90 px tall on a 128 px canvas.

![](https://assets.spritopia.com/blog/pixel-sailor-2/baked7-cmp.png)

## 5. Stop the sway, keep the bob

The mocap walk sways the hips, and the head with them, about 3.5 px each step. In pixels that is the whole body sliding back and forth.

Shifting each frame to pin the head works, but the feet slide. Pinning the face was worse, because rounding the eyes and rounding the head disagreed by a pixel every other frame. Jin's suggestion was to fix it in the rig. Each frame, the hips move back under the head's average position, and each thigh leans just enough that its ankle stays planted. Only the legs' lean changes from frame to frame, and the feet don't move.

![](https://assets.spritopia.com/blog/pixel-sailor-2/steady3.gif)

The bob stays. It is quantized on the camera's pixel grid, and a part only steps once its bone is 0.75 px away, so the top of the bob doesn't flick 58, 59, 58.

![](https://assets.spritopia.com/blog/pixel-sailor-2/bob.gif)

## 6. One drawing, live in the studio

All of this started as Python run over PNG dumps on our GPU box. That meant the studio, where Jin tunes characters, couldn't show it. So the pipeline moved to TypeScript and now runs in the browser on the render buffers directly. Against the Python it matches pixel for pixel on coverage and fills; only the lines differ, because they changed on purpose. A frame takes about 30 ms, and the full bake dropped from 178 s to 99 s.

![](https://assets.spritopia.com/blog/pixel-sailor-2/ts-vs-py.png)

The studio has a Pixel tab. Turn on *pixel art* and she plays as the variant draws her. The sliders cover the face's shades, shade spacing, highlight dots, outline darkness, the arm line, priority, when a part steps and when it redraws, size, and the sway fix. Each change redraws the loop once the knob rests. *Soft pixels* shows her blurred slightly when scaled, the way Ragnarok's client draws its sprites.

![](https://assets.spritopia.com/blog/pixel-sailor-2/studio-pixel-tab.png)

## What's left

- The chin's outline can read as a mouth.
- In the game she is still drawn with nearest sampling. Ragnarok's softness needs bilinear filtering, and that needs the transparent pixels round her filled with her own colours first.
- Her hair still freezes while the head's drawing is held.

She is the sailor's third variant on the playtest: [playtest.spritopia.com](https://playtest.spritopia.com/).

![](https://assets.spritopia.com/blog/pixel-sailor-2/run-8dir.gif)
