REFERENCE
- Gallery page: https://magpui.com/en/i/keycap-ascii-fish-0a1668
- First frame (image): https://dnioievkvxejjbxgwvko.supabase.co/storage/v1/object/public/media/keycap-ascii-fish-0a1668/shot.jpg
- Clip (mp4): https://dnioievkvxejjbxgwvko.supabase.co/storage/v1/object/public/media/keycap-ascii-fish-0a1668/clip.mp4
- Original post: https://x.com/kail_designs/status/2115435415855624593 by @kail_designs
Before building, download the first frame and look at it. If ffmpeg is available, download the clip and extract frames (ffmpeg -i clip.mp4 -vf fps=2 frame-%02d.png), then study the motion frame by frame. Match what you see; the text below describes it, the frames are the truth.
BUILD: Build a full-screen dark canvas animation where two fish silhouettes swim across a faint grid, each fish assembled from small keycap-style tiles that show ASCII characters. The shapes continuously morph and rearrange: tiles join and leave the silhouette, so the fish seems to be built from glittering typed characters. It should feel nerdy, calm and a little magical.
DESIGN SYSTEM: Background deep navy #060a18 with a barely visible square grid (1px lines at 6% white, cell about 14px). Each keycap tile is a rounded square (about 12px, radius 2-3px) filled slate blue (#3b4a73 to #8fa3d6 depending on density), with a 1px lighter top edge, a subtle inner shadow and a centered monospace glyph (about 9px) in pale blue-white (#dfe8ff). A small share of glyphs are accent colored: yellow #f2c94c, pink #ff5fa2, white. Glyph ramp from dense to light: @ # 8 % X S t : ; ! o ' . Sparse stray tiles and tiny dots/circles around the fish at 20-40% opacity. Fish are about 25-30% of viewport width each, in different orientations (one diagonal up-left, one swimming right).
MOTION: Each frame, a fish mask (a shape with a sine-wave body deformation, tail amplitude about 12% of body length, 1.2 Hz) is sampled onto the tile grid; tile brightness maps to the glyph ramp so the body shows denser glyphs in the middle and lighter at fins. Tiles entering the mask pop in over 160 ms with a spring scale from 0.6 to 1 (overshoot to 1.1) and flicker through 2-3 random glyphs every 50 ms before settling; tiles leaving shrink and fade over 220 ms, drifting outward 4-10px as loose particles. The fish drift slowly (about 20 px/s) and gently turn; the two fish rearrange with a 6-8 s loop and cross each other occasionally. Easing: cubic-bezier(0.34, 1.56, 0.64, 1) for pop-in, ease-out for fade.
SECTIONS: A single canvas stage. States: idle swim; optional pointer hover pushes nearby tiles away (radius about 90px) and they return over 600 ms.
IMPLEMENTATION: One Canvas 2D (or React + Canvas 2D) with a pre-rendered offscreen glyph atlas for each glyph/color pair so each tile is one drawImage; typed arrays for tile state; devicePixelRatio aware, 60fps, pause when offscreen via IntersectionObserver. Fish masks can be low-res procedural shapes or a pre-baked silhouette image sampled per frame. prefers-reduced-motion: render one static frame. Expose props for tile size, palette, glyph ramp and swim speed. Canvas gets role=img with an aria-label.
ADAPT TO THE PROJECT: Reuse the project's existing design tokens, components and fonts; map colors to the closest existing tokens; never copy logos, brand names, photos or illustrations from the original; use placeholders.
SUCCESS CHECK:
- The silhouettes are clearly readable as fish while tiles continuously pop in and out.
- Glyphs flicker briefly on arrival and then stay stable; no full-canvas flashing.
- Runs at 60fps with thousands of tiles and shows a static frame under reduced motion.