workshop private

← all creations

Gaffer

sprites · created 2026-09-04

48×36 pixel sprite — a glassblower's bench lit only by the glass, with three loops including the one where you stop turning.

pixel-artcanvas

A hot shop at night, drawn as one sprite: brick furnace on the left with the glory hole cut into it, bench rails across the middle, the gaffer seated at the right end of the pipe, jacks and shears hanging off the rafter, a bucket on the floor that is about to be missed.

Three loops:

What’s different about this one

Every other sprite in this catalog resolves a character to a fixed palette colour and that is the pixel. This one paints the whole shop in near-black — the palette has no warm entries at all — and then runs a light pass over the 48×36 buffer before it blits anything:

That is why the cooling is legible rather than asserted. Nothing in the sag frames says “this is going wrong”; the room just gets darker, band by band, as the number in heat comes down.

The one composition rule everything else bends around: the glass must stay the whitest thing on screen, even while it is sitting inside a furnace. The mouth interior is deliberately capped well below the gob’s temperature (0.62 against 0.97) and the gather frames park the ball half in and half out of the opening, or the subject disappears into its own light source.

Reuse

Render at integer scales with nearest-neighbour filtering or the dither bands smear into the gradient they were there to avoid. gather plays at 4 fps, blow at 4.5, sag at 3.5 (ANIMATIONS carries all three). blow and sag are one-shots by intent — the demo plays either once and falls back to gather.

Source

No .aseprite — the canonical source is source/gaffer.mjs: a framework-free ES module holding the shop, the gaffer’s six poses and the gob shapes as character maps, plus the light pass. Edit a grid and regenerate everything with:

node source/render.mjs

(needs site/node_modules installed — the script resolves Playwright from there.) That one command rewrites export/, media/, thumb.png, and the demo’s bundled copy of the module, so nothing drifts.

How a frame is built

  1. SHOP, straight off the character map — flue, rafter, hanging tools, furnace masonry, bench rails, seat, bucket, floor. All of it near-black.
  2. A steel collar ring, then the mouth disc painted inside it. Drawing the collar first is what makes the opening read as a hole in a wall rather than a second sun hovering in front of one.
  3. The blowpipe — before the gaffer, so his hands close over it.
  4. The gaffer: one of six 14×20 poses (roll-a, roll-b, lean, puff, rest, slump).
  5. The gob at the end of the pipe, then anything that has left it — a blob in the air, a splat on the floor.
  6. Embers, which are lights as well as pixels.
  7. The light pass over everything not marked emissive.
  8. Blit, one fillRect per pixel.

A frame declaration is therefore a short list — pose, pipe position, gob shape, heat, how hard the furnace is breathing — not a second copy of the artwork.

Gotchas

Port notes

paintFrame writes into a plain RGB buffer and only touches a 2D context at the blit, so the light pass ports anywhere you can address pixels — same shape as The Claw’s and lab-mouse’s grid walking, one step further. In DragonRuby, keep the grids as strings and either run the same pass into args.outputs.solids or precompute it: the frames are static, so the honest port is just consuming the spritesheet with source_x = frame * 48.