workshop

← all creations

Warehouse Duck

sprites · created 2026-08-28

24×20 pixel sprite — a rubber duck whose waterline reads query load, from a calm float to fully submerged.

pixel-artcanvas

A classic yellow rubber duck, floating in water that does the storytelling: a calm two-frame bob at idle, a lower three-frame waddle with wings held out under load, and — at overload — sunk to just a periscope bill and eye above the surface. Nods to AWS acquiring DuckLabs and pulling the DuckDB team in-house.

Reuse

Render at integer scales with nearest-neighbor filtering or the edges smear. calm plays at 2 fps, waddle at 5 fps, submerged at 1.5 fps (ANIMATIONS in the module carries all three).

Source

No .aseprite — the canonical source is source/warehouse-duck.mjs: a framework-free ES module holding the pixel grid as a character map (one char per pixel, palette at the top). Edit the 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

One static DUCK grid — head, bill, eye, body, wing, tail — is painted at a per-frame (duckTop, duckLeft) offset, then a water layer is drawn on top from waterlineRow down, clipping whatever part of the duck sank below the surface. Raising waterlineRow frame to frame is the entire “submerging” effect — no separate underwater art. Rows 8–10 (the wing band) swap to a wider dark patch when a frame sets wing: 'spread', reading as wings held out during waddle without a second full body grid. A shifting modulo pattern on ripple gives the water a moving-foam texture at a fixed waterline.

Gotchas

Port notes

paintFrame is grid-walking with no DOM dependency beyond a 2D context — same shape as court-ace’s and hot-dog-cart’s — so a DragonRuby port translates directly: keep the grid as strings, map cells to args.outputs.solids, or just consume duck-sheet.png as a spritesheet with source_x = frame * 24.