The unofficial fifth member of the Straphanger / Stand Clear / nyc-conductor platform: a rounded, low-slung rat with a pointed snout, one visible ear, and a thin curling tail. Two animation loops — an idle sit (gentle bob, an eye that blinks shut on the second frame) and a scurry (four-frame run cycle, legs alternating plant/lift, tail flicking side to side).
Reuse
export/rat-sheet.png— 180×14 strip, six 30×14 frames in order:idle-0,idle-1,scurry-0,scurry-1,scurry-2,scurry-3.rat-{idle,scurry}-*.png— the individual frames.rat@8x.png— prescaled 8× hero shot for docs/previews.
Render at integer scales with nearest-neighbor filtering or the edges smear.
Idle plays at 2 fps, scurry at 8 fps (ANIMATIONS in the module carries both).
Source
No .aseprite — the canonical source is source/subway-rat.mjs: a
framework-free ES module holding the pixel grids as character maps (one char
per pixel, palette at the top). 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 the frames are built
A frame is three layers composited on the 30×14 canvas, back to front: TAIL
(fixed anchor at the rump), LEGS (fixed anchor, peek out from under the
torso), then BODY on top at the frame’s bob offset — the only layer that
moves vertically. Same split as scout-drone’s glow/dome: only the parts that
actually move need more than one grid. Six frames come from one body grid,
three leg patterns (stand, strideA, strideB), three tail patterns
(curl, swishA, swishB), and an eye-color swap for the blink — not six
full redraws.
Gotchas
validateGrids()throws if any row isn’t exactly 30 chars, or a frame references a missing leg/tail pattern or eye color — call it after editing, or just runrender.mjs, which calls it first.demo/subway-rat.jsis generated. Editsource/subway-rat.mjsand re-render.- The demo draws the sprite from the character maps at runtime, fine at this
size. A game should blit
rat-sheet.pnginstead.
Port notes
paintFrame is ~25 lines of grid walking with no DOM dependency beyond a 2D
context — same shape as scout-drone’s and mech-strider’s — so a DragonRuby
port translates directly: keep the grids as strings, map cells to
args.outputs.solids, or just consume rat-sheet.png as a spritesheet with
source_x = frame * 30.