Singels private Blog
head

Let Opus create HTML-Animations

The prompt:

Create a single self-contained HTML file that renders an animated pixel art cat curling up and going to sleep, using vanilla JavaScript and Canvas 2D. No external assets, libraries, or network requests.

RENDERING

  • Draw everything to an offscreen canvas at a fixed logical resolution of 128x96, then blit to a fullscreen display canvas scaled by the largest integer factor that fits the window, centered, with imageSmoothingEnabled = false and CSS image-rendering: pixelated.
  • All drawing snaps to integer coordinates on the logical canvas. No sub-pixel positions, anti-aliasing, gradients, or shadowBlur.
  • Fixed palette of ~24 hex colors: warm cozy indoor tones (soft wooden floor, warm rug, orange/ginger cat palette, deep night window backdrop). Every pixel comes from this palette.

CHARACTER

  • Build the cat procedurally from filled rects and pixel runs, ~24x20 logical pixels: pointed ears, curved tail, multi-shade fur with darker outline, snout and eyes.
  • Parameterize the pose (body curl amount, leg placement, ear twitches, tail sway, breathing chest motion). Animate parameters smoothly, then quantize to the pixel grid each frame so motion reads at an 8-12 fps pixel animation feel even though the loop runs at 60fps.

ANIMATION

  • Looping state machine: IDLE (sitting, tail swish, blinking) -> STRETCH (arched back, paws extended, yawn) -> LIE_DOWN (transitioning down into a curled circle) -> SLEEP (deep rhythmic chest breathing, floating "Zzz" particles) -> WAKE (uncurling back up). Ease pose parameters between keyframes.
  • Pooled allocation-free particle system: preallocate and reuse. Floating pixel-art "Zzz" particles spawn during SLEEP, gently drifting upward and fading. Snap particle positions to the grid when drawing.
  • Fixed 60hz timestep update with rAF rendering. Zero object allocation inside the loop.

SCENE

  • Cozy indoor setting: warm floorboards, a small oval rug where the cat rests, a subtle wall/floor horizon line, and a small window showing stars in the background. Character silhouette must read clearly.
  • Subtle warm ambient lighting, with a slight warm glow shifting around the cat during the sleep phase.

QUALITY BAR

  • Crisp pixels at any window size, seamless loop, stable 60fps, readable silhouette. Should look like a polished 16-bit sprite animation, not vector shapes scaled down.

Claude’s answer:

Your cat is live. It sits and swishes its tail, stretches with a yawn, lowers into a curl, sleeps with 1px breathing and drifting Zzz's, then wakes and sits back up. The whole loop takes about 24 seconds and restarts seamlessly.

How it's built:

  • Palette-locked framebuffer. Instead of drawing with Canvas fill styles, everything is written as palette indices into a 128×96 Uint8Array. Once per frame that buffer is converted to RGBA through a lookup table. Off-palette pixels are impossible by construction.
  • Lighting without gradients. The lamp halo, the warm glow around the cat, the contact shadow and the vignette all use a 4×4 Bayer dither. Each lit pixel steps to its neighbour in a light or dark palette ramp, which gives the classic 16-bit look.
  • Procedural cat. The body, chest and head are shaded ellipses with rim light, belly and tabby stripes. Legs are 2px lines, ears are stepped triangles, and the tail is a chain of 10 segments. An automatic outline pass then wraps the whole silhouette. The head gets its own inner dark ring so it still reads when it's tucked against the body in the curl.
  • Pose system. Three keyframes (sit, stretch, curl) are stored as 28-float arrays and blended with cubic easing. The pose is sampled on a 10fps time grid and snapped to half-pixels, so motion steps like hand-made sprite frames even though the loop runs at 60hz.
  • Zero allocation in the loop. Everything is preallocated: typed arrays, the 8-slot particle pool, and the pose buffers. The fixed 60hz update runs inside a rAF accumulator.
  • Display. Output is scaled by the largest integer factor that fits, and it accounts for devicePixelRatio, so pixels stay crisp on retina screens too.

Timing tweaks: the durations live in DUR (IDLE, STRETCH, LIE_DOWN, SLEEP, WAKE). Lengthen SLEEP there if you want a longer nap before it wakes. Your cat is live. It sits and swishes its tail, stretches with a yawn, lowers into a curl, sleeps with 1px breathing and drifting Zzz's, then wakes and sits back up. The whole loop takes about 24 seconds and restarts seamlessly.

How it's built:

  • Palette-locked framebuffer. Instead of drawing with Canvas fill styles, everything is written as palette indices into a 128×96 Uint8Array. Once per frame that buffer is converted to RGBA through a lookup table. Off-palette pixels are impossible by construction.
  • Lighting without gradients. The lamp halo, the warm glow around the cat, the contact shadow and the vignette all use a 4×4 Bayer dither. Each lit pixel steps to its neighbour in a light or dark palette ramp, which gives the classic 16-bit look.
  • Procedural cat. The body, chest and head are shaded ellipses with rim light, belly and tabby stripes. Legs are 2px lines, ears are stepped triangles, and the tail is a chain of 10 segments. An automatic outline pass then wraps the whole silhouette. The head gets its own inner dark ring so it still reads when it's tucked against the body in the curl.
  • Pose system. Three keyframes (sit, stretch, curl) are stored as 28-float arrays and blended with cubic easing. The pose is sampled on a 10fps time grid and snapped to half-pixels, so motion steps like hand-made sprite frames even though the loop runs at 60hz.
  • Zero allocation in the loop. Everything is preallocated: typed arrays, the 8-slot particle pool, and the pose buffers. The fixed 60hz update runs inside a rAF accumulator.
  • Display. Output is scaled by the largest integer factor that fits, and it accounts for devicePixelRatio, so pixels stay crisp on retina screens too.

Timing tweaks: the durations live in DUR (IDLE, STRETCH, LIE_DOWN, SLEEP, WAKE). Lengthen SLEEP there if you want a longer nap before it wakes.