English Β·
Deutsch
A browser port of TerminalTextEffects (TTE), the Python terminal-effects library - rebuilt in dependency-free vanilla JavaScript and shipped as a single file you can drop into any page.
Live demo - every effect runs right in your browser.
The effects come in five groups:
- Text effects run on a plain
<pre>block by rewriting its text content - no canvas, the text stays selectable, box-drawing characters stay aligned, and the colour is inherited from your element (perfect for an already-green console). - Canvas effects lay a temporary canvas over the element for free 2D character motion (fireworks, black hole, rain, ...), then fade it out and reveal the untouched text below.
- Style effects (
crt,colorshift,highlight,gradient) recolour or light the element in place without ever touching the text, so they layer cleanly over an already-visible console. - Input effects (
placeholder) live inside a form field: they type its placeholder line by line and rewrite nothing else - not the value, not the styling, not the markup. - Art effects (
aura) are the exception: they do not read text from the page, they generate it. Any Unicode glyph becomes an ASCII figure wrapped in a moving ring of characters, and the animation keeps running until you cancel it.
<pre id="log">=== System ready ===</pre>
<script src="retro-text-effects.min.js"></script>
<script>
RetroTextEffects.decrypt('#log');
</script>That is the whole integration: one script tag exposes window.RetroTextEffects,
then call an effect with an element or a CSS selector.
One thing about the font. The scramble effects cycle through block and shade glyphs (
ββββ), andmatrix/matrix2add katakana. If the font of the target element does not contain them, the browser pulls them from a fallback font with a different advance width - the lines then shear apart column by column while the effect runs, and block-letter banners come out mangled. Use a font that has the glyphs (Consolas, DejaVu Sans Mono, Menlo, or a complete JetBrains Mono - the usual latin webfont subsets leave them out), or pass your ownglyphspool.
These only rewrite the element's textContent, frame by frame:
| Effect | What it does |
|---|---|
decrypt(el, opts) |
Cells flicker through random glyphs, then lock onto the final text. |
print(el, opts) |
Reveals the text in reading order with a moving print head. |
matrix(el, opts) |
Each column resolves top-to-bottom behind a falling bright glyph. |
overflow(el, opts) |
Rows scroll and reshuffle, then settle into order. |
errorcorrect(el, opts) |
Character pairs start swapped in the wrong place and swap back one by one. |
randomsequence(el, opts) |
The characters appear one after another in completely random order. |
middleout(el, opts) |
The text grows from the centre of the block outward. |
sweep(el, opts) |
A noisy band sweeps left to right and leaves the resolved text behind. |
pour(el, opts) |
The text fills up from the bottom row like a liquid, snaking back and forth. |
slide(el, opts) |
The rows slide in as blocks, alternating from the left and the right. |
burn(el, opts) |
An ember front with a ragged edge eats through the block and burns the text in. |
vhstape(el, opts) |
Glitch bands shift rows sideways and sprinkle noise until the tracking settles. |
wipe(el, opts) |
A straight wavefront sweeps across in a chosen direction and leaves the resolved text behind. |
slice(el, opts) |
Each row is cut in half; the two pieces slide inward until they butt together in the middle. |
waves(el, opts) |
An undulating crest rolls across the block and resolves the text as it passes. |
These lay a temporary canvas over the element, animate the characters in free 2D motion, then fade out and reveal the untouched text:
| Effect | What it does |
|---|---|
matrix2(el, opts) |
The classic falling-glyph screen: katakana rain, then reveal (canvas twin of matrix). |
decrypt2(el, opts) |
Canvas twin of decrypt: the resolved text emerges bright from dimmed ciphertext. |
print2(el, opts) |
Canvas twin of print: a glowing print head with a hot afterglow on fresh characters. |
overflow2(el, opts) |
Canvas twin of overflow: the block spins past sub-pixel-smooth and decelerates onto the text. |
beams(el, opts) |
Bright beams sweep the rows and columns, then a wipe brings the text to full brightness. |
rain(el, opts) |
Every character falls from above straight into its place. |
bouncyballs(el, opts) |
Characters drop in as coloured balls and bounce into position. |
bubbles(el, opts) |
Every character floats down in its own bubble, swaying, and pops into place. |
scattered(el, opts) |
Characters start scattered across the stage and glide to their spot. |
expand(el, opts) |
The whole text bursts outward from the centre. |
spray(el, opts) |
A nozzle in the corner sprays the characters onto the block in curved arcs. |
swarm(el, opts) |
The characters arrive in wobbling swarms that settle area by area. |
fireworks(el, opts) |
Rockets launch, explode into sparks and throw their characters to the text. |
blackhole(el, opts) |
Characters spiral into a singularity, then erupt back out to the text. |
rings(el, opts) |
Characters orbit on concentric spinning rings, then disperse to their positions. |
unstable(el, opts) |
The text shakes, explodes towards the edges and reassembles. |
laseretch(el, opts) |
A laser beam traces the text in and throws off falling sparks. |
binarypath(el, opts) |
Characters enter as streams of 0/1 and travel to their spot at right angles, then resolve. |
crumble(el, opts) |
The text crumbles into scattered dust, then the dust is vacuumed up and reforms. |
orbittingvolley(el, opts) |
Four orbiting launchers fire the characters inward, filling the text from the centre out. |
smoke(el, opts) |
A wall of drifting smoke rolls across and leaves the characters colourised behind it. |
spotlights(el, opts) |
Spotlights sweep the dark block, converge on the centre and flood it to reveal the text. |
synthgrid(el, opts) |
A neon grid grows from the centre, the characters fill in diagonally, then the grid fades. |
thunderstorm(el, opts) |
Lightning bolts strike the block one after another, lighting up the text slab by slab. |
These recolour or light the element in place - they never touch the textContent,
so they layer cleanly over an already-visible console:
| Effect | What it does |
|---|---|
crt(el, opts) |
Persistent CRT treatment - phosphor glow, scanlines, faint flicker. cancel() removes it. |
colorshift(el, opts) |
Persistent animated gradient that keeps sliding across the glyphs. cancel() removes it. |
highlight(el, opts) |
Runs a single specular highlight across the text, then restores the original colours. |
gradient(el, opts) |
Paints a colour gradient across the glyphs - top to bottom, left to right, diagonal or at a free angle. cancel() removes it. |
gradient is the one to reach for when the text should simply look good rather than move.
It paints the glyphs through background-clip: text, so the text itself stays untouched and
selectable. The gradient is measured against the text, not the block, so a left-to-right run
spans exactly the widest line instead of trailing off into the empty half of a <pre>.
// Top to bottom, smooth, warm amber
RetroTextEffects.gradient('#log', { direction: 'down', palette: 'amber' });
// Left to right through the whole vaporwave palette
RetroTextEffects.gradient('#log', { direction: 'right', palette: 'vaporwave' });
// One hard colour band per text line
RetroTextEffects.gradient('#log', { mode: 'bands', palette: 'rainbow', repeat: 'lines' });
// Diagonal stripes that keep flowing
RetroTextEffects.gradient('#log', {
direction: 'diagonal', mode: 'stripes', palette: 'cyberpunk', size: 14, animate: true,
});
// Your own colours, at a free angle
RetroTextEffects.gradient('#log', { direction: 37, colors: ['#ff0055', '#00e5ff'] });| Setting | Values |
|---|---|
direction |
down (top to bottom), up, right (left to right), left, diagonal, diagonal-up, or any CSS angle as a number |
mode |
smooth (blend), bands (hard-edged colour blocks), stripes (fixed-width stripes, size in px) |
palette |
phosphor, amber, ice, fire, toxic, gold, copper, sunset, vaporwave, cyberpunk, rainbow, mono - or pass colors |
repeat |
how often the palette fits along the axis - 'lines' gives exactly one colour per text line |
animate |
false by default. true lets the whole pattern flow along its own direction, at speed |
The twelve built-in palettes:
| Palette | Colours | Reads as |
|---|---|---|
phosphor |
#0f8f2e #33ff33 #ccffcc |
green screen, dark to bright |
amber |
#a35a00 #ffb000 #ffe9b0 |
the other classic tube |
ice |
#2f6fb0 #3fa7d6 #d6f6ff |
cold blue, deep to pale |
fire |
#c02a4a #e4572e #ffa62b #ffef7a |
embers to sparks |
toxic |
#3f8f10 #7cff00 #e4ff9e |
radioactive green |
gold |
#b08000 #ffd700 #fff6c0 |
polished metal |
copper |
#8a4a1e #b87333 #ffd9a0 |
warm patinated metal |
sunset |
#5b2b9e #7b2ff7 #ff3c78 #ff8c42 |
violet into orange |
vaporwave |
#05ffa1 #01cdfe #b967ff #ff71ce |
the full 80s spectrum |
cyberpunk |
#00f0ff #7a5cff #ff007a |
neon cyan to magenta |
rainbow |
#ff3b30 #ff9500 #ffee00 #33ff33 #00cfff #6633ff #ff33cc |
seven colours, best with bands |
mono |
#ffffff #5a5a5a |
white to grey, no hue at all |
All of this is readable at runtime, so a picker never has to duplicate the values:
RetroTextEffects.gradientPalettes, gradientDirections and gradientModes return the
valid names as arrays, and gradientColors('sunset') hands back that palette's colours.
placeholder is the only effect that runs on a form field. It types the placeholder of an
<input> or <textarea> character by character, holds it, wipes it out again and moves on
to the next line. The only thing it ever writes is the placeholder attribute - no wrapper
element, no injected markup, no inline styles - so the field keeps exactly the CSS you gave it.
| Effect | What it does |
|---|---|
placeholder(el, opts) |
Types, holds and deletes the placeholder of an input or textarea, line after line. |
<input id="search" placeholder="Search the docs">
<!-- or keep the lines in the markup, so the field still reads sensibly without JS: -->
<input id="cmd" data-rte-placeholders="ssh root@mainframe | cat /var/log/retro.log">RetroTextEffects.placeholder('#search', {
texts: ['Search the docs', 'or an effect name', 'try: fireworks'],
cps: 24, // typing speed
cursor: '_', // '' switches the cursor off
});
const fx = RetroTextEffects.placeholder('#cmd'); // lines from data-rte-placeholders
fx.cancel(); // stops it and puts the original placeholder backThree details that make it behave in a real form: it freezes while the field is focused or
filled in (a placeholder nobody can see does not need to flicker, and a blinking one under a
cursor is only in the way) and picks up again on blur, it honours prefers-reduced-motion by
setting the first line as a plain placeholder, and cancel() restores the placeholder the
field was served with.
aura is the odd one out twice over. It does not read the element's text, it rasterises a
Unicode glyph on an offscreen canvas, translates the cells into a character ramp and wraps
the figure in a ring of terminal characters that keeps moving. Like crt it is persistent -
it runs until cancel().
It is also the only effect here that is not part of the TTE port: TerminalTextEffects has no counterpart for it. The idea comes from the animated ASCII ghost on ghostty.org, the website of the Ghostty terminal emulator. Rebuilt from scratch for arbitrary glyphs, no code taken from there.
| Effect | What it does |
|---|---|
aura(el, opts) |
Turns a glyph into a glowing ASCII figure with an animated aura. Also offers update() and text(). |
asciiArt(glyph, opts) |
Not an effect but a plain function - returns the art as a string, ready for any other effect. |
const ghost = RetroTextEffects.aura('#stage', { emoji: 'π»', cols: 46 });
ghost.update({ variant: 'sonar', motion: 'spin', color: '#35d0ff' });
ghost.text(); // the figure as plain text, without the aura
ghost.cancel(); // stops it and restores the element's original content
// Or skip the animation and feed the art into any other effect:
document.querySelector('#stage').textContent = RetroTextEffects.asciiArt('π', { cols: 40 });
RetroTextEffects.decrypt('#stage');Six aura variants, listed in RetroTextEffects.auraVariants:
| Variant | What it does |
|---|---|
shimmer |
Every cell flickers on its own - the classic look from ghostty.org. |
pulse |
Brightness follows the distance, so the aura breathes outward. |
sonar |
Rings leave the figure one after another. |
orbit |
An arc of light circles the figure like a radar sweep. |
updraft |
A noise field drifts upward, like rising heat. |
halo |
No movement, just the stepped distance. |
Three kinds of motion, listed in RetroTextEffects.auraMotions: off keeps the figure
still, float tilts it and hops it by whole rows, and spin squeezes it horizontally
with a cosine so it reads as a flipping coin. The poses are rasterised once and replayed
as a loop - a CSS transform would tilt the characters themselves and break the grid.
Two things worth knowing: emoji fonts are platform specific, so the same glyph looks
different on Windows, macOS and Linux - bake the art in with asciiArt() if you need a
fixed result. And prefers-reduced-motion is honoured: the figure is then drawn once and
stands still.
Each effect returns a small controller:
const fx = RetroTextEffects.print('#log', { cps: 80, onDone: () => {} });
fx.cancel(); // stop early
await fx.finished; // resolves when the animation ends| Option | Type | Default | Applies to |
|---|---|---|---|
speed |
number | 1 |
all |
onDone |
function | - | all |
fps |
number | 30 |
text effects (canvas effects run delta-timed on rAF) |
glyphs |
string | built-in pool | decrypt, decrypt2, matrix, matrix2, sweep |
preserveWhitespace |
boolean | true |
decrypt |
cps |
number | 60 / 22 |
print, print2, placeholder |
head |
string | β |
print |
cycles |
number | 3 |
overflow, overflow2 |
ratio |
number | 0.1 |
errorcorrect (share of swapped pairs) |
band |
number | 6 |
sweep (width of the noise band) |
duration |
number (ms) | 1500 |
matrix2 |
fontSize |
number | 20 |
matrix2 |
color |
string | #00ff00 / #33ff33 |
matrix2, crt, synthgrid, highlight |
scanlineOpacity |
number | 0.15 |
crt |
glow |
boolean | true |
crt |
flicker |
boolean | true |
crt |
direction |
string | number | diagonal / right / down |
wipe (left/right/up/down/diagonal), highlight (left/right), gradient (also diagonal-up or an angle) |
amplitude |
number | 4 |
waves (how far the crest bends per row) |
colors |
string[] | retro palette | colorshift, gradient (overrides palette) |
palette |
string | phosphor |
gradient (twelve built-in colour patterns) |
mode |
string | smooth |
gradient (smooth/bands/stripes) |
repeat |
number | lines |
1 |
gradient ('lines' = one colour per text line) |
size |
number (px) | 18 |
gradient (stripe width, mode: 'stripes') |
animate |
boolean | false |
gradient (lets the pattern flow along its direction) |
texts |
string[] | from the field | placeholder (else data-rte-placeholders, else the placeholder) |
deleteCps |
number | 45 |
placeholder (deleting speed) |
hold |
number (ms) | 1800 |
placeholder (pause on the finished line) |
pause |
number (ms) | 400 |
placeholder (pause on the empty field) |
cursor |
string | _ |
placeholder ('' switches it off) |
blink |
boolean | true |
placeholder |
loop |
boolean | true |
placeholder (false stops after the last line) |
emoji |
string | π» |
aura |
cols |
number | 40 |
aura, asciiArt (raster width in characters) |
variant |
string | shimmer |
aura |
motion |
string | float |
aura (off/float/spin) |
width |
number | 4.5 |
aura (aura width in line heights) |
fit |
boolean | true |
aura (scales the font so the figure fills the element), gradient (spans the text, not the block) |
ramp |
string | Β·~oxX%$@ |
aura, asciiArt |
All other canvas effects take speed and onDone; font, colour and character grid
are read from the target element so the hand-off to the real text is seamless.
glyphs is also accepted by decrypt2 and matrix2.
The build is a single self-contained file with no dependencies and no external
requests, so there is nothing to hotlink. Download retro-text-effects.min.js
from the releases
and include it with a plain script tag:
<script src="retro-text-effects.min.js"></script>
<script>
RetroTextEffects.decrypt('#log');
</script>That works anywhere you can serve a static JS file - static sites, offline apps, or a server-rendered view where you drop it in as a local asset.
npm install
npm run lint
npm run build # -> dist/retro-text-effects.js + dist/retro-text-effects.min.jsOpen demo/index.html in a browser to see the showroom.
Apache-2.0. This is a browser port of TerminalTextEffects (Python, MIT) - the effects were rebuilt from scratch for the DOM, no code was copied.