A street-corner fixture: a striped umbrella on a pole, a stainless box with a serving window and ledge, two wheels underneath. A wisp of steam curls up past the umbrella’s peak at rest — then a gloved hand rises up beside the pole with a hot dog held high before dropping back out of sight.
Reuse
export/cart-sheet.png— 162×27 strip, six 27×27 frames in order:idle-0,idle-1,serve-0,serve-1,serve-2,serve-3.cart-{idle,serve}-*.png— the individual frames.cart@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, serve at 5 fps (ANIMATIONS in the module carries both).
Source
No .aseprite — the canonical source is source/hot-dog-cart.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
Three layers, back to front. CART is one static grid — umbrella, pole,
box, wheels — painted a few rows down from the frame’s top edge, leaving
headroom above the umbrella’s peak for the second layer: STEAM, a small
grid anchored over that peak whose pattern (not position) carries the
drift, the same trick scout-drone’s glow bar uses for its sweep. The
third layer, ARM, is a small local grid — glove plus hot dog — placed at
a per-frame (armX, armY) offset instead of a fixed anchor, exactly like
court-ace’s racket arm; idle frames simply carry arm: null and skip
the layer entirely. The arm’s travel is capped so its highest point sits
just below the umbrella — it rises alongside the pole, never through the
canopy.
Gotchas
validateGrids()throws if the cart grid isn’tFRAME_Wwide, a frame references a missing steam pattern, or an arm offset would draw outside the frame — call it after editing, or just runrender.mjs, which calls it first.demo/hot-dog-cart.jsis generated. Editsource/hot-dog-cart.mjsand re-render.- The demo draws the sprite from the character maps at runtime, fine at
this size. A game should blit
cart-sheet.pnginstead.
Port notes
paintFrame is grid-walking with no DOM dependency beyond a 2D context —
same shape as court-ace’s and scout-drone’s — so a DragonRuby port
translates directly: keep the grids as strings, map cells to
args.outputs.solids, or just consume cart-sheet.png as a spritesheet
with source_x = frame * 27.