Typewriter Glitch Gradient Reveal — Kinetic Text JS
Typewriter Glitch Gradient Reveal · Animations · Plain HTML, CSS & JS · Live preview
Copy & paste
Source Code
<div class="scene">
<h1 class="reveal-heading">
<span id="typed"></span><span class="cursor" id="cursor">|</span>
</h1>
<button class="replay" onclick="playSequence()">Replay</button>
</div>* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: 'Courier New', monospace; background: #08080c; display: flex; align-items: center; justify-content: center; min-height: 100vh; padding: 20px; }
.scene { text-align: center; display: flex; flex-direction: column; align-items: center; gap: 26px; }
.reveal-heading {
font-size: clamp(28px, 7vw, 54px);
font-weight: 800;
letter-spacing: 0.02em;
color: #e2e8f0;
min-height: 1.3em;
}
.cursor { animation: blink 0.85s step-end infinite; font-weight: 300; color: #22d3ee; }
@keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } }
#typed.glitching {
animation: glitchShake 0.35s steps(2, jump-none) 4;
text-shadow: -2px 0 #ec4899, 2px 0 #22d3ee;
}
@keyframes glitchShake {
0% { transform: translate(0); }
25% { transform: translate(-2px, 1px); }
50% { transform: translate(2px, -1px); }
75% { transform: translate(-1px, -1px); }
100% { transform: translate(0); }
}
#typed.final {
background: linear-gradient(90deg, #22d3ee, #6366f1, #ec4899, #22d3ee);
background-size: 300% auto;
-webkit-background-clip: text; background-clip: text;
-webkit-text-fill-color: transparent;
text-shadow: none;
animation: shimmer 4s linear infinite;
}
@keyframes shimmer { 0% { background-position: 0% center; } 100% { background-position: 300% center; } }
.replay {
padding: 9px 20px; font-size: 13px; font-weight: 600;
background: #1e293b; color: #e2e8f0; border: 1px solid #334155; border-radius: 999px;
cursor: pointer; transition: background 0.15s, transform 0.1s; font-family: system-ui, sans-serif;
}
.replay:hover { background: #334155; }
.replay:active { transform: scale(0.96); }const TEXT = 'SHIP IT TODAY';
const GLITCH_CHARS = '#%&$@!*<>/?~';
const typed = document.getElementById('typed');
const cursor = document.getElementById('cursor');
function wait(ms) {
return new Promise((r) => setTimeout(r, ms));
}
async function typeOut(text) {
typed.textContent = '';
for (let i = 0; i < text.length; i++) {
typed.textContent += text[i];
await wait(55);
}
}
async function glitchBurst(text) {
typed.classList.add('glitching');
const frames = 10;
for (let f = 0; f < frames; f++) {
typed.textContent = text
.split('')
.map((ch) => (ch === ' ' ? ' ' : (Math.random() < 0.5 ? GLITCH_CHARS[Math.floor(Math.random() * GLITCH_CHARS.length)] : ch)))
.join('');
await wait(45);
}
typed.textContent = text;
typed.classList.remove('glitching');
}
async function playSequence() {
cursor.style.opacity = '1';
typed.className = '';
await typeOut(TEXT);
await wait(400);
cursor.style.opacity = '0';
await glitchBurst(TEXT);
typed.classList.add('final');
}
playSequence();Extend with AI
Take This Snippet Further With AI
The AI prompt for this one is for Patreon supporters ($3 a month, 7 days free), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.
What's included
Features
About this UI Snippet
Typewriter Glitch Gradient Reveal — Three-Stage Kinetic Typography Sequence

This snippet chains three distinct text effects into one choreographed sequence: a character-by-character typewriter reveal, a burst of glitch text noise, and a settle into a shimmering animated gradient — three techniques that normally exist as separate snippets, combined into a single kinetic typography moment with a clear beginning, middle, and end. The point is not any one stage alone but the transition between them: the eye reads the sequence as "the headline is typing... now it's malfunctioning... now it's resolved into something finished and premium," which is a stronger narrative arc than any single effect provides on its own.
Stage one: the typewriter
typeOut(text) appends one character to typed.textContent every 55ms inside an await wait(55) loop, identical in spirit to the standalone typewriter snippet's tick() approach but written with async/await since this sequence has several distinct phases that must run strictly in order.
Stage two: the glitch burst
Once typing finishes and a brief 400ms pause elapses, glitchBurst(text) adds a .glitching class (which layers a chromatic-aberration-style text-shadow in cyan and pink plus a shaky glitchShake keyframe using steps(2, jump-none) for a deliberately jittery, non-smooth motion) and runs 10 frames where roughly half of each character is randomly swapped for a symbol from GLITCH_CHARS before being restored to the real text. This is a condensed, faster cousin of the full text scramble decode — here the point is a quick visual "malfunction," not a gradual left-to-right reveal.
Stage three: the gradient settle
After the glitch burst restores the correct text and removes .glitching, .final is added. This applies the same four-declaration background-clip: text gradient shimmer technique used in animated gradient text — linear-gradient background, background-size: 300% auto, background-clip: text, and -webkit-text-fill-color: transparent — animated by the shimmer keyframe. Landing on this state reads as the headline "resolving" into its final, polished form after the disruption of the glitch.
Why async/await instead of chained setTimeout
Because this sequence has four ordered phases (type, pause, glitch, settle) each with its own internal timing, writing it as one async function playSequence() with await at each step keeps the control flow linear and readable top to bottom, and makes the "Replay" button a single, safe call — typed.className = '' at the top resets all state before the whole sequence reruns from scratch.
Step by step
How to Use
- 1Watch it load, then replayThe headline types out, glitches briefly, then settles into a shimmering gradient. Click "Replay" to reset and watch the full sequence again.
- 2Change the headline textIn the JS panel, update const TEXT = "SHIP IT TODAY" to your own phrase.
- 3Adjust the typing speedChange the 55 in await wait(55) inside typeOut() — smaller values type faster.
- 4Adjust the glitch intensityIn the JS panel, change frames (currently 10) for a longer or shorter glitch burst, or the 0.5 probability threshold for how many characters glitch per frame.
- 5Change the gradient colorsEdit the linear-gradient stops on #typed.final in the CSS panel to match your brand palette.
- 6Export in your formatClick "HTML" for a standalone file, "JSX" for a React component, or "Tailwind" for a React + Tailwind version.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
playSequence() is written as a single async function that awaits each stage in turn: await typeOut(TEXT), a paused await wait(400), await glitchBurst(TEXT), then a synchronous class toggle for the gradient. Because each stage function itself returns a promise that resolves when its own internal timing finishes, the whole four-stage sequence reads as flat, linear code instead of deeply nested setTimeout callbacks.
glitchBurst() runs a fixed number of frames (10). On each frame, it maps over every character of the target text and, with roughly 50% probability per character (Math.random() < 0.5), replaces it with a random symbol from GLITCH_CHARS; otherwise it keeps the real character. After the last frame, the text is force-set back to the correct string so no glitch state can leak into the final display.
The blinking cursor is a visual cue that the headline is "still typing." Once typing is done, cursor.style.opacity is set to 0 before the glitch begins, since a blinking cursor next to a glitching, unstable string of characters would read as a bug rather than an intentional effect.
The glitchShake keyframe uses steps(2, jump-none) instead of the default smooth easing. steps() timing makes the transform jump between discrete positions rather than interpolating smoothly, which reads as a jittery, mechanical malfunction rather than a soft wobble.
In playSequence(), remove the await glitchBurst(TEXT) line and the cursor.style.opacity = "0" line before it, then call typed.classList.add("final") directly after the typing pause.
Yes. Click "JSX" for a React component. Manage a "stage" state (typing, glitching, final) with useState, drive the async sequence from a useEffect that runs once on mount, and derive the applied CSS class name from the current stage value.