21/07/2026
We turned our homepage into an ASCII galaxy. Here's the engineering.
Most agency homepages are a hero image, three cards and a testimonial slider. Ours is a navigable galaxy rendered entirely in text characters. This is the build log — the real one, with the numbers.
The pipeline: WebGL → canvas → characters
The scene is ordinary Three.js: star field, point-cloud galaxies, sprite labels, an orbit camera. The trick is that you never see the WebGL output directly. Every frame goes through an ASCII filter:
- Render the 3D scene to a WebGL canvas as usual.
- Draw that canvas downscaled onto a tiny 2D canvas — one pixel per character cell.
- Read the pixels, convert each to luminance, and map brightness onto a 67-character ramp:
' .'at the dark end,%B@$at the bright end. - Dump the result into a
<pre>element as one big string.
The <pre> is where the color comes from — and this is the part people ask about. The text itself is transparent. A radial gradient is painted through it with background-clip: text, so the whole galaxy shares one continuous gradient no matter which characters are on screen. One CSS rule, zero per-character styling.
Why it doesn't melt your phone
A naive version of this melts phones. Ours doesn't, because the budget is explicit:
- The ASCII pass runs at 22fps, decoupled from the 60fps WebGL render. Text doesn't need 60fps to feel alive; your eye smooths it out.
getImageDatais the expensive call, so the character grid is tiny — the canvas is sized in characters, not pixels (roughly 1/6 of the viewport per axis at our font size).willReadFrequently: trueon the 2D context keeps the readback on the CPU path instead of ping-ponging the GPU.- Everything pauses when it can't be seen — an
IntersectionObserverand the Page Visibility API halt the loop when the stage is off-screen or the tab is hidden.
The calm pass
The first versions were too alive. A tan() in the text shader produced unbounded spikes — cool in a demo, exhausting on a page you actually use. We replaced it with sin(), halved the idle chromatic offset, and narrowed the star twinkle range so stars breathe instead of strobing. Motion design for text is still motion design.
What the crawlers see
Here's the uncomfortable part most WebGL showcases skip: a canvas full of characters is invisible to search engines. Our fix is architectural, not cosmetic — the galaxy is a client-side layer mounted on top of a fully server-rendered page. Same data feeds both: the nine client projects orbiting the PROJECTS galaxy come from the same file that renders the crawlable case-study pages below it. The show never blocks the content.
If you want an experience like this without giving up your rankings — that's literally our job. Talk to us.