Matrix Rain
Matrix-style code rain built entirely with CSS animations and the DOM: columns of katakana and digits fall in a continuous loop on a black background — no canvas, no requestAnimationFrame.
You'll get access to the interactive demo with a free account.
This effect is part of Effect.Labs — 811 vanilla effects, some free, some premium. Atmosphere has 57 effects, including 10 free. Explore the category →
3 usage examples



How it works
The falling motion is driven entirely by @keyframes matrixFall, which slides each .matrix-column from top: -100% to top: 100%. No requestAnimationFrame loop is needed — the browser handles progression at its native refresh rate, keeping the JS thread free and CPU usage low.
writing-mode: vertical-rl and text-orientation: upright stack glyphs vertically, each displayed upright. Paired with font-family: monospace, every character occupies a fixed-width cell — the column grid aligns perfectly with no position math.
Each column self-recycles via animationend: as it exits the viewport, the element removes itself (remove()) and spawns a fresh one at a random horizontal position, duration, and opacity. The pool stays constant with no DOM node accumulation.
Three independent variables simulate 2D depth: horizontal position (left: 0–100%), fall duration (3–8 s — shorter = closer), opacity (0.3–1.0). No 3D transform or Z-ordering required. Characters are drawn from a chars array mixing digits (0–9) and katakana (ア…ン), which must be defined before initialization.
Accessibility
- prefers-reduced-motion: not implemented. The animation runs regardless of system settings — the integrator should add a
(prefers-reduced-motion: reduce)media query to hide or stop the columns. - The
.matrix-containercarries neitheraria-hidden="true"norrole="presentation": column text will be read by screen readers if focus enters the container. Addaria-hidden="true"on integration. - Character contrast:
#0f0(pure green) on#000(black) → ratio ≈ 15.3:1, well above WCAG AAA (7:1). The glow effect (text-shadow) further enhances readability. - No keyboard-interactive elements inside the effect — no focus trap. If CTA content is layered on top, it stays in the normal tab order.
- Continuous full-screen motion may trigger vestibular discomfort in some users: avoid fullscreen use without a static fallback.
Browser compatibility
Relies on CSS animations, vertical writing-mode, and standard DOM manipulation — supported everywhere with no polyfill.
If CSS animations are unsupported, the <code>.matrix-container</code> renders as an empty black rectangle — no JS error. The page remains usable.
The code
HTML structure to paste into your page (CSS + JS available with a premium account):
<div class="matrix-container" id="matrixContainer">
<!-- .matrix-column elements are injected and recycled by JS.
The container must have position:relative, overflow:hidden
and a defined height (px or vh). -->
</div>
Full HTML + CSS + JS, copy-paste ready — with hundreds of premium effects.
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
| color (CSS .matrix-column) | #0f0 | Character color. Swap to #00d4ff (cyan), #f43f5e (red), or #f59e0b (amber) to shift the mood. |
| text-shadow (CSS .matrix-column) | #0f0 0 0 5px | Glow halo. Increase the spread (e.g. 0 0 12px) for a strong neon look, reduce to 0 0 2px for a subtle render. |
| font-size (CSS .matrix-column) | 14px | Character size and therefore column density. 10 px = narrow packed columns, 20 px = wide airy columns. |
| background (CSS .matrix-container) | #000 | Container background. Switch to rgba(0,0,0,0.85) to let a parent background show through, or #0a0a1a for a blue-tinted black. |
| animationDuration (JS) | 3 + 5 * Math.random() + "s" | Fall duration range (3–8 s). Narrow to 1 + 2*Math.random() for a fast stream, 6 + 4*Math.random() for a slow drifting feel. |
| opacity (JS) | .3 + .7 * Math.random() | Column opacity range (0.3–1.0). Lower the floor to 0.1 for a very subtle background effect. |
| chars (JS, constant to define) | katakana + 0–9 | Character sampling array. Replace with Latin letters, binary symbols ("01".split('')) or emoji to customise the visual identity. |
| n (JS, column length) | 10 + Math.floor(20 * Math.random()) | Characters per column (10–30). Raise the max (e.g. 40) for longer columns that overflow the container height. |
FAQ
requestAnimationFrame loop. The browser optimises declared CSS animations on the compositor thread without blocking JS. The trade-off: no per-pixel control (progressive head fade, colour shift along column length) that a canvas would allow.getElementById('matrixContainer') — one element per page. For multiple instances, write a factory function that accepts a DOM element as a parameter (e.g. function createColumnFor(container)) and closes over that container rather than the global id. The CSS (.matrix-column, @keyframes matrixFall) is shared without conflict.animationDelay at creation time: t.style.animationDelay = -(Math.random() * dur) + 's' (where dur is the duration in seconds). A negative delay starts the animation mid-run — columns appear instantly at varied heights instead of all launching from the top simultaneously.