A terminal animation that renders a paragraph of text with a shimmer — a bright band of light that sweeps left to right across every line simultaneously, like the loading indicator in the Claude CLI.
The paragraph is stored as a single string and word-wrapped in software to a fixed column width of 60 characters. Words are never broken mid-word.
function wrap(text, width) {
const words = text.split(' ');
const lines = [];
let line = '';
for (const word of words) {
if (line && line.length + 1 + word.length > width) {
lines.push(line);
line = word;
} else {
line = line ? line + ' ' + word : word;
}
}
if (line) lines.push(line);
return lines;
}
Each character has two possible colors: a dim base color and a bright peak color, both defined as RGB. The actual color of any given character is a blend between these two, determined by how close that character is to the shimmer's current position.
const BASE = { r: 80, g: 80, b: 120 }; // muted indigo
const PEAK = { r: 230, g: 235, b: 255 }; // near-white
function colorAt(ci, shimmerPos) {
const dist = Math.abs(ci - shimmerPos);
const t = smoothstep(Math.max(0, 1 - dist / SHIMMER_WIDTH));
const r = lerp(BASE.r, PEAK.r, t);
const g = lerp(BASE.g, PEAK.g, t);
const b = lerp(BASE.b, PEAK.b, t);
return `\x1b[38;2;${r};${g};${b}m`;
}
Distance — how far the character's column is from the shimmer center.
Normalize & flip — maps distance to 0–1 and flips it so the center scores 1 (brightest).
Smoothstep — passes the value through t²(3 - 2t), an S-curve that makes the glow rise and fall softly instead of linearly.
Blend — interpolates each RGB channel between BASE and PEAK using the smoothed value.
Emit — packs the result into an ANSI truecolor escape sequence.
On the first frame, lines are written normally with no trailing newline, leaving the cursor at
the end of the last line. On every subsequent frame, two escape sequences reposition it:
\x1b[NA moves the cursor up N−1 rows, then \r resets it to column 0.
The entire block is then overwritten character by character. All output for a frame is assembled
into a single string before writing to avoid partial-frame flicker.
setInterval(() => {
if (pausing) {
if (++pauseFrames >= PAUSE_FRAMES) {
pausing = false; pauseFrames = 0; pos = -SHIMMER_WIDTH;
}
return;
}
render(pos);
pos += 0.4;
if (pos > WRAP_WIDTH + SHIMMER_WIDTH) pausing = true;
}, 16);
16 ms intervals give ~60 fps. Each tick advances the shimmer 0.4 columns. When the sweep exits the right edge, a 45-frame (~750 ms) pause runs before the position resets. This rhythm — sweep, pause, sweep — makes it feel like a living indicator rather than a mechanical loop.
Full annotated walkthrough: WALKTHROUGH.md