Turning WebGL into ASCII, every frame
This titled the HypeDuel arena over in ai-arena, and it's a neat trick: real 3D text, rendered with shaders, then converted to ASCII in the browser every single frame.
Move your cursor over it — the plane tilts and the colors hue-shift to follow.
Two canvases doing different jobs
It starts ordinary. The word gets drawn to an offscreen 2D canvas, uploaded as
a texture, and mapped onto a subdivided PlaneGeometry. A vertex shader rolls
the vertices on sine waves; a fragment shader nudges the R, G, and B channels by
slightly different amounts so the edges chromatically shimmer.
transformed.x += sin(time + position.y) * 0.5 * waveFactor;
transformed.y += cos(time + position.z) * 0.15 * waveFactor;
transformed.z += sin(time + position.x) * waveFactor;The ASCII pass
Here's the part that makes it. Each frame, the WebGL output is drawn into a second, tiny canvas — sized so one pixel equals one output character. Then every pixel is read back, converted to brightness, and mapped to a glyph from a ramp ordered dark-to-light:
const gray = (0.3 * r + 0.6 * g + 0.1 * b) / 255;
const idx = Math.floor((1 - gray) * (charset.length - 1));
str += charset[idx]; // " .:-=+*#%@" style ramp, just longerThe whole string drops into a <pre> with mix-blend-mode: difference and a
gradient clipped to the text. Cursor position feeds both the plane's rotation
and a hue-rotate on the container, so the thing tracks the mouse with zero
per-character work.
Keeping it off the server
three runs in the browser only. On Cloudflare Workers, importing it into the SSR
graph crashes the render, so the component is a thin loader that pulls the heavy
scene in with a dynamic import() inside useEffect. Same pattern I used for
the Infinite Terrain scene.
From B3's ai-arena; the underlying effect comes from the React Bits community. Grab it on the ASCII Text page.
Ask your agent to implement this
Read the full writeup at https://seangeng.com/writing/ascii-text-from-webgl.md and implement it in my project.
It covers: Turning WebGL into ASCII, every frame — A three.js plane rendered to a hidden canvas, then read pixel-by-pixel and rewritten as ASCII characters in a <pre>. The trick is sampling the render at one pixel per glyph. From B3's ai-arena.
Requirements:
- Follow the technique/approach exactly as described in the writeup.
- Adapt names, colors, and styling to my project's existing conventions.
- If it's a component, make it reusable with sensible props and TypeScript types.
- Keep it accessible: semantic HTML, keyboard support, and respect prefers-reduced-motion.
- When done, tell me which files you created or changed and how to use it.Paste into Claude Code, Codex, Cursor, or any agent. view raw .md download source .zip