Creature sprites · The process

A few pixels must produce
thousands of movements.

A Baldur’s Gate II character can be only a few dozen pixels tall. The goal is not to redraw her. It is to keep the same silhouette, colours and animation, with a cleaner result on a modern screen.

  • ×2chosen scale
  • 10,323frames for CHFF4
  • 8,527sprite BAMs inventoried

The two usual choices

Nearest stays sharp,
while Linear becomes blurry.

Nearest keeps every square. Linear mixes neighbouring pixels. One looks too blocky; the other washes away the small details.

Native ×1
Original CHFF4 human female fighter frame
The exact game pixels.
Nearest ×2
Human female fighter enlarged to x2 with nearest-neighbour scaling
Clean blocks, hard stair-steps.
Linear ×2
Human female fighter enlarged to x2 with bilinear scaling
Smoother, but details melt together.

Why not SeedVR?

The sprites are too small
to be processed with SeedVR.

SeedVR works well when an image contains enough material. Here, a face can fit into four or five pixels. A generative model would have to invent the eyes, armour or weapon again on every frame.

For maps, reconstruction helps. For sprites, guessing changes the character.
Actual sizeOriginal human female fighter frame at its true size
×12 viewOriginal fighter frame enlarged with visible pixels

A tool made for pixel art

The same frame is compared
with nine methods.

ScalePix runs locally and puts several specialised algorithms side by side. The retained recipe is xBR2x, one pass, without antialiasing.

The same neutral human fighter frame compared with original, Nearest, Bilinear, EPX, MMPX, HQX and xBR2x scaling methods
xBR2x reads diagonals and curves more clearly while the selected mode keeps the existing palette colours.

Why ×2, not ×4

The useful gain happens at ×2.

Once the display filter is applied, ×4 did not look different enough to justify its cost. Compared with ×2, every frame contains four times as many pixels.

×21 frame · selected
×44× the pixels
The same fighter frame compared at native scale, xBR x2 and xBR x4
Direct outputs from the same source frame, displayed at the same size.

Never one big sprite sheet

Every frame is processed
separately.

Each image is processed on its own. Order, animation cycles, centre points and transparency must remain exactly where the engine expects them.

  1. 01

    Read

    Open the original BAM and its structure.

  2. 02

    Extract

    Separate every cycle and frame.

  3. 03

    Upscale

    Run xBR2x once, without AA.

  4. 04

    Verify

    Keep centres, alpha and frame order.

  5. 05

    Catalogue

    Give the engine the matching HD frame.

Five separate CHFF4 animation frames from the same action
CHFF4 alone: 23 BAM resources, 1,170 cycles and 10,323 individual frames.
Batch pipeline

Claude and Codex helped build the tools that inventory, extract, process and verify these frames. The repeatable work runs in batches; the visual decisions stay human.

False colours, real constraints

These colours act as
references for the engine.

The bright green, pink, red and blue areas are instructions. The game replaces them at runtime with the character’s chosen colour ranges.

Those indices have to survive the upscale. Adding arbitrary shades or painting a new gradient would break reliable recolouring.

Five CHFF4 source frames showing the false-colour palette zones
The strange source colours are useful: they tell the engine what it may replace.
Metal
Minor
Major
Skin
Leather
Armour
Hair

Another kind of sprite

The goblin already contains
its real colours.

Unlike the playable fighter, this goblin is not false-colour palettised. Its green skin, clothes and equipment are stored directly in the frame, so the comparison appears naturally coloured.

A coloured goblin frame compared across nine ScalePix upscaling methods
The same ScalePix test, this time on a non-palettised creature. The xBR2x recipe stays the same.

A character is assembled

The game assembles the body
and its equipment.

The game does not store every final combination. It picks the layers matching the equipped items, aligns their direction and frame, then builds one character for the draw.

4 bodies
Human fighter body layer
Body
31 families
Sword equipment layer
Weapon
12 families
Shield equipment layer
Shield
19 families
Helmet equipment layer
Helmet
Human female warrior equipped with armour, helmet, shield and swordFinal assembly in game

The last step happens in the engine

xBR creates the pixels before
Catmull-Rom displays them.

Catmull-Rom is a controlled cubic interpolation. At −0.25, it softens the hardest steps without spreading the image like Linear.

Dshaders 0.3.5 was built around the older game pipeline and could not simply be dropped into 2.7. The game did contain a Catmull-Rom path, but it was made for opaque images and forced alpha to full. Transparent sprites needed their own integration.

01xBR ×2 textureSharper shapes
02Catmull −0.25Gentle reconstruction
03ScreenLogical ×1 geometry

Final proof · In game

The warrior is compared
in three render modes.

Three matched captures: same area, pose and colours. Only the sprite rendering mode changes.

Human female warrior at native size with nearest filteringNative sprite ×1NearestExact scene · exact pose
Human female warrior at native size with linear filteringNative sprite ×1LinearExact scene · exact pose
Human female warrior enlarged two times and displayed with Catmull-Rom at minus 0.25 sharpnessxBRZ sprite ×2Catmull-Rom · sharpness −0.25No normal outline
What gets checked in gameMovementAttacksDirectionsEquipmentPalettesTransparencyOcclusion

The result I am after

The final sprite stays faithful
to the original character.

No new character design, no invented details — just the original animation treated with more care on a modern display.