ASCII Earth
Open the globe. Drag to change the viewpoint; automatic rotation pauses during the drag and resumes afterward. Zoom and pan are disabled. The same component appears on the homepage and in the Three.js sketches.
How the image becomes text
The underlying object is a Three.js sphere, not a flat map. A shader uses two textures: an ocean mask distinguishes water from land, while a relief map adds variation within the land. Oceans render black; land brightness varies with the relief. This is a visual treatment, not a simulation of sunlight or day and night.
Three.js AsciiEffect then renders that sphere, downsamples the image and converts brightness values into characters. The visible result is an HTML text grid. The shader produces the image to sample; it does not draw the characters itself.
const effect = new AsciiEffect(renderer, ' .:-+*=%~@#01o', {
invert: true,
resolution: 0.16,
});
The leading space keeps dark ocean regions empty. Land remains visible without drawing a solid disk around it. Some viewpoints therefore show only a narrow strip of continents; a mostly empty hemisphere is not, by itself, evidence that rotation has stopped.
The characters differ in shape and visual density, allowing variations in brightness to remain visible in text. Horizontal strokes such as -, = and ~ can form bands when repeated across terrain of similar brightness. Font metrics and spacing also affect the image: the HTML table is a character grid, so ordinary table borders and cell shading would interfere with the globe.
Rotation and lifetime
The globe rotates at 0.28 radians per second, with drawing limited to about 30 frames per second. OrbitControls changes the camera view during pointer interaction. The globe uses the same pause/play state and frame-timing pattern as the attractor. Both start moving when opened, independently of the browser's reduced-motion preference. The visible Pause/Play button controls movement explicitly. Manual dragging remains available while paused.
Off-screen and hidden-page drawing is skipped. On unmount, the component releases its animation frame, observers, event listeners, controls, textures, geometry, material and WebGL renderer.
A valid container can still produce an empty sample
AsciiEffect uses floor(width × resolution) and floor(height × resolution) for its sampling canvas. Clamping the outer dimensions to one pixel is therefore insufficient: with a resolution of 0.16, the sampling width can still become zero. Calling getImageData then throws an error.
The component rounds the display dimensions first, then skips sizing and drawing until both can produce at least one sample pixel. Checking before rounding misses values such as 6.3 pixels, which round to 6 and still produce a zero-sized sample. This matters when a preview is hidden, a tab changes or a container is being laid out. A later valid resize restores rendering.
When changing this experiment, check more than one screenshot: watch different longitudes, drag and release, check autoplay with reduced motion enabled, try pause/play, hide and restore the container, and switch away and back. The homepage starts with default settings and supports dragging the globe. It hides parameter panels and playback buttons to leave room for the image; its title opens the experiment page with full controls. Implementation lives in src/components/AsciiEarth/index.js; its layout styles must not add borders or shading to the text grid.