The unofficial eleventh member of the Straphanger / Stand Clear / nyc-conductor / Subway Rat / Platform Pigeon / Subway Cockroach / Platform Cat / Subway Raccoon / Subway Seagull platform crew: a round, sitting squirrel with tufted ears and a bushy tail carried up over its back, working on something it clearly stole off the platform floor. Two animation loops — an idle (still body, gentle bob, an eye that blinks shut on the second frame) and a nibble (four-frame cycle: paws bring the food up to the mouth and back down, with the tail giving an occasional flick) — the actual thing a squirrel does when it’s found something to eat, not a walk cycle. Unlike the rest of the crew, this one never moves its feet at all.
Reuse
export/squirrel-sheet.png— 156×22 strip, six 26×22 frames in order:idle-0,idle-1,nibble-0,nibble-1,nibble-2,nibble-3.squirrel-{idle,nibble}-*.png— the individual frames.squirrel@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, nibble at 6 fps (ANIMATIONS in the module carries both).
Source
No .aseprite — the canonical source is source/subway-squirrel.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 four layers composited on the 26×22 canvas, back to front: TAIL
(a local grid, fixed anchor at the shoulder, arched up over the back rather
than dragging), LEGS (fixed anchor, haunches planted — this one sits, so
there’s a single sit pattern instead of a gait), BODY (head, ears and
torso, at the frame’s bob offset), then ARMS on top (paws + acorn, held
at the chest) — the only layer that changes between “chewing” and “resting”
independently of everything else. The body silhouette itself was generated
from overlapping ellipses (head, neck, torso) with the outline traced
automatically off the filled shape, rather than freehand pixel-by-pixel —
freehand columns drift out of alignment fast on a body this round, and a
hand-placed eye or ear reads as a stray mark the moment the silhouette
around it is off by even one column.
Gotchas
validateGrids()throws if any row isn’t exactly 26 chars (16 forTAIL, 10 forARMS), a frame references a missing tail/arm pattern or eye color, or a layer would overflow the canvas — call it after editing, or just runrender.mjs, which calls it first.demo/subway-squirrel.jsis generated. Editsource/subway-squirrel.mjsand re-render.BODYis intentionally short (17 rows, not the full 22) so its haunch clears out well aboveLEG_Y— sinceLEGSis drawn beforeBODY, any overlap between the two just gets painted over and the feet vanish. Grow the torso ellipse and check the gap beforeLEG_Yis still positive.- The demo draws the sprite from the character maps at runtime, fine at this
size. A game should blit
squirrel-sheet.pnginstead.
Port notes
paintFrame is grid-walking with no DOM dependency beyond a 2D context —
same shape as the rest of the platform crew’s — so a DragonRuby port
translates directly: keep the grids as strings, map cells to
args.outputs.solids, or just consume squirrel-sheet.png as a spritesheet
with source_x = frame * 26. The one wrinkle versus subway-rat/
subway-raccoon is the extra ARMS layer drawn after BODY instead of
before it — carry that draw order over, or the acorn ends up hidden behind
the chest instead of held in front of it.