More Animations Snippets
Split Text Animation — Free HTML CSS JS Snippet
Split Text Animation · Animations · Plain HTML, CSS & JS · Live preview
What's included
Features
About this UI Snippet
Split Text Animation — Word Reveal, Character Slide-Up, Line Wipe & Scramble Decode

Split text animations are one of the most in-demand effects on modern landing pages and creative portfolios. They make large display headings (especially gradient text) feel dynamic and intentional — the text appears to emerge from below, one word or character at a time, creating a cinematic reveal that draws the eye and communicates design quality. This snippet provides four distinct split text animation variants: word-by-word reveal, character slide-up with rotation, single-line wipe, and a scramble decode effect — all in plain HTML, CSS, and vanilla JavaScript.
Word-by-word reveal
The text is split into words. Each word is wrapped in two spans: an outer .word-wrap with overflow: hidden, and an inner .word that starts at translateY(110%) — completely below the clip boundary. A CSS @keyframes animation moves each word to translateY(0). Staggered animation-delay (0, 0.09s, 0.18s...) per word creates the sequential reveal. The overflow: hidden on the wrapper makes the word appear to "wipe up" from below rather than fade in.
Character slide-up with rotation
Identical architecture to word reveal but split by character. Each character starts at translateY(110%) rotate(8deg) and animates to translateY(0) rotate(0). The 8° starting rotation adds a slight snap as each character lands upright. The stagger is 40ms per character — tight enough to feel unified, loose enough to see each character individually.
Line wipe reveal
The entire line starts at translateY(100%) inside an overflow: hidden container. A single animation moves the line from below the container to its natural position. No splitting needed — the overflow clip creates the wipe-up effect at the full line level. This is the fastest and most impactful variant for short display text.
Scramble decode
A requestAnimationFrame loop runs for 1200ms. Progress (0 to 1) determines how many characters have been "revealed" to their final value. Unrevealed characters show random characters from a 70-char pool each frame, creating the scramble effect. As progress increases, more characters lock into their final positions from left to right.
Combining multiple variants for a sequence
Chain all four variants with increasing base delays for a coordinated reveal sequence: splitWords(headline, 0); splitChars(subheading, 0.6); wipeReveal(tagline, 1.0); scramble(cta, 1.4). The scramble variant is the same effect as the standalone text scramble. The delays create a natural reading order — headline first, supporting text follows. Use this pattern for full-screen hero sections like the word-flip hero where each text element appears sequentially.
Build with AI
Build, Understand, Optimize, and Extend It With AI
You don't need to work out why four different reveal techniques all lean on the same clip trick by hand. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly why translateY(110%) inside an overflow hidden wrapper reads as a "wipe up" rather than a fade, or how the scramble function's requestAnimationFrame loop decides how many characters have locked in versus how many are still randomized based on elapsed time. The same assistant can help optimize it, for example checking whether the character-split variant's 40ms-per-character stagger becomes uncomfortably slow for long strings and whether it should switch to word-splitting past some length. It's also useful for extending the feature: ask it to trigger each variant via IntersectionObserver instead of only on page load or button click, respect prefers-reduced-motion by skipping straight to the final text, or add a fifth variant like a blur-to-sharp reveal. Treat the code less like a finished artifact and more like a starting point for a conversation.
Prompt to recreate it
Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:
Build four distinct text reveal animations in plain HTML, CSS, and JavaScript, no framework, no libraries: a word-by-word reveal, a character slide-up, a single-line wipe, and a scramble decode.
Requirements:
- For the word reveal, split an element's text content on whitespace into individual words, wrap each word in an outer span with overflow hidden and an inner span that starts transformed fully below the wrapper (translateY well over 100 percent), then animate the inner span to translateY(0) via a CSS keyframe, staggering each word's animation-delay by a fixed increment based on its index.
- For the character slide-up, do the same clip-and-slide technique per character instead of per word, but also start each character rotated a few degrees and animate both the rotation and the vertical position back to neutral together, using a shorter per-character stagger increment than the per-word one.
- For the line wipe, do not split the text at all — wrap the whole line in a single overflow hidden container and animate the entire line from translateY(100%) to 0 as one unit.
- For the scramble decode, run a requestAnimationFrame loop for a fixed duration that computes an elapsed-time-based progress fraction, reveals characters from the start of the string up to a count derived from that progress, and fills all not-yet-revealed character positions with a random character from a large character pool on every single frame (leaving actual space characters as spaces, never randomized).
- Provide a single function that resets and restarts all four animations together with staggered base delays so they play in a coordinated sequence rather than simultaneously, and make sure any in-flight scramble animation frame is properly cancelled before a restart.
- As a documented accessibility improvement in a code comment, show how checking prefers-reduced-motion would skip directly to each element's final text with no animation at all.Step by step
How to Use
- 1Click "↺ Replay all" to see all four animations restartThe word reveal, character slide-up, line wipe, and scramble decode all run in sequence with staggered starts. Each animation has a different timing — observe how each creates a different visual impression.
- 2Use the word reveal for hero headlinesCall splitWords(el, baseDelay) where el is your headline element. The function splits text into words, wraps each in overflow:hidden spans, and applies staggered CSS animation delays. Works for any text length.
- 3Use character split for short impact textCall splitChars(el, baseDelay) for 2-4 word text. Best for short, punchy text — "Bold. Fast. Free." The character rotation adds energy. Avoid for long text where 40ms×40 chars = 1.6s total before the last character lands.
- 4Trigger animations on scroll with IntersectionObserverWrap the replayAll() call in an IntersectionObserver: const obs = new IntersectionObserver(([e]) => { if(e.isIntersecting) { replayAll(); obs.disconnect(); } }, { threshold: 0.3 }); obs.observe(document.querySelector(".stage")).
- 5Change animation timing and easingUpdate cubic-bezier(0.16,1,0.3,1) in the @keyframes CSS for different feels. This is a spring-style ease-out. Use ease for softer, linear for mechanical. Change 0.6s duration to 0.4s for snappier or 1s for more dramatic reveals.
- 6Export in your formatClick "HTML" for a standalone file, "JSX" for a React component using useEffect to trigger on mount or scroll, or "Tailwind" for a React + Tailwind CSS version.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
The .word-wrap (or .line-wipe-wrap for the line variant) has overflow: hidden. Inside it, the text element starts at transform: translateY(110%) — 110% of the element height, positioning it completely below the container's bottom edge. Because overflow is hidden, the text is invisible. As translateY animates to 0, the text rises from below the invisible clip boundary into view. The result looks like the text is wiping up from the baseline — a much more polished effect than a simple fade-in.
Use IntersectionObserver: const elements = document.querySelectorAll(".split-words, .split-chars"); const obs = new IntersectionObserver(entries => { entries.forEach(entry => { if (entry.isIntersecting) { if (entry.target.classList.contains("split-words")) splitWords(entry.target); else splitChars(entry.target); obs.unobserve(entry.target); } }); }, { threshold: 0.3 }); elements.forEach(el => obs.observe(el)). The obs.unobserve() prevents re-animating on scroll back.
Add: if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) { document.getElementById("split-words").textContent = "Design with intention"; return; } at the top of replayAll(). For CSS: @media (prefers-reduced-motion: reduce) { .word, .char { animation: none; transform: none; opacity: 1; } .line-wipe { animation: none; transform: none; } }. This makes all text immediately visible without animation for users who have enabled the system accessibility setting.
Click "JSX" to download. Run the split functions in a useEffect on mount: useEffect(() => { splitWords(wordsRef.current, 0); splitChars(charsRef.current, 0.4); ... }, []). Use useRef for each animation target element. For scroll-triggered animations, add an IntersectionObserver inside the useEffect. Return a cleanup: return () => cancelAnimationFrame(scrambleFrame) to cancel any ongoing rAF loop when the component unmounts.