GSAP provides specialized plugins for text animation, including character-by-character effects, typewriter animations, and scrambling effects. These plugins enable sophisticated typography animations.
import { TextPlugin, SplitText, ScrambleTextPlugin } from "gsap/all";
gsap.registerPlugin(TextPlugin, SplitText, ScrambleTextPlugin);Create typewriter and text replacement effects with customizable timing.
interface TextVars {
text?: string; // Target text content
delimiter?: string; // Character delimiter for typing effect
speed?: number; // Typing speed override
diff?: boolean; // Only animate differences between old and new text
padSpace?: boolean; // Add spaces to maintain layout during typing
}Usage Examples:
// Basic typewriter effect
gsap.to(".typewriter", {
duration: 3,
text: "Hello, World!",
ease: "none"
});
// Custom delimiter for word-by-word typing
gsap.to(".word-by-word", {
duration: 2,
text: "One word at a time",
delimiter: " " // Type word by word instead of character by character
});
// Morphing text (only changes animate)
gsap.to(".morphing-text", {
duration: 1.5,
text: "New text content",
diff: true // Only animate the differences
});
// Preserve layout while typing
gsap.to(".layout-text", {
duration: 2,
text: "This maintains spacing",
padSpace: true
});Split text into individual characters, words, or lines for granular animation control.
/**
* SplitText constructor for splitting text elements
* @param targets - Text elements to split
* @param vars - Split configuration
*/
class SplitText {
constructor(targets: string | Element | Element[], vars?: SplitText.Vars);
// Properties
chars: Element[]; // Array of character elements
words: Element[]; // Array of word elements
lines: Element[]; // Array of line elements
original: Element[]; // Original elements
// Methods
split(vars?: SplitText.Vars): SplitText; // Re-split with new options
revert(): SplitText; // Restore original text
}
interface SplitText.Vars {
type?: "lines" | "words" | "chars" | "lines,words" | "lines,words,chars";
linesClass?: string; // CSS class for line elements
wordsClass?: string; // CSS class for word elements
charsClass?: string; // CSS class for character elements
tag?: string; // HTML tag for split elements (default: "div")
reduceWhiteSpace?: boolean; // Reduce whitespace between elements
}Usage Examples:
// Split into characters
const splitText = new SplitText(".text", { type: "chars" });
// Animate each character
gsap.from(splitText.chars, {
duration: 1,
y: 50,
opacity: 0,
stagger: 0.05,
ease: "back.out(1.7)"
});
// Split into words and lines
const splitWords = new SplitText(".paragraph", {
type: "lines,words",
linesClass: "line",
wordsClass: "word"
});
// Animate lines sliding in
gsap.from(splitWords.lines, {
duration: 1,
x: -100,
opacity: 0,
stagger: 0.2
});
// Character reveal animation
const chars = new SplitText(".reveal-text", { type: "chars" });
gsap.set(chars.chars, { opacity: 0, rotationX: -90 });
gsap.to(chars.chars, {
duration: 0.8,
opacity: 1,
rotationX: 0,
stagger: 0.02,
ease: "back.out(1.7)"
});Create scrambling and decoding text effects with customizable character sets.
interface ScrambleTextVars {
scrambleText?: {
text?: string; // Target text
chars?: string; // Character set for scrambling
revealDelay?: number; // Delay before revealing each character
speed?: number; // Scrambling speed
delimiter?: string; // Character delimiter
rightToLeft?: boolean; // Reveal from right to left
tweenLength?: boolean; // Animate text length changes
};
}Usage Examples:
// Basic scramble effect
gsap.to(".scramble", {
duration: 2,
scrambleText: {
text: "DECODED MESSAGE",
chars: "XO", // Use only X and O for scrambling
revealDelay: 0.5
}
});
// Matrix-style effect
gsap.to(".matrix-text", {
duration: 3,
scrambleText: {
text: "THE MATRIX HAS YOU",
chars: "01", // Binary characters
revealDelay: 0.6,
speed: 0.3
}
});
// Custom character set
gsap.to(".custom-scramble", {
duration: 2.5,
scrambleText: {
text: "CUSTOM DECODE",
chars: "!@#$%^&*()+=",
revealDelay: 0.3,
rightToLeft: true // Reveal from right to left
}
});Combining text plugins with other GSAP features for complex effects.
// Typing with cursor effect
const typewriter = gsap.timeline();
gsap.set(".cursor", { opacity: 1 });
typewriter
.to(".typewriter-text", {
duration: 2,
text: "Hello, World!",
ease: "none"
})
.to(".cursor", {
opacity: 0,
duration: 0.5,
repeat: -1,
yoyo: true
}, 0);
// Word cloud animation
const words = new SplitText(".word-cloud", { type: "words" });
gsap.set(words.words, {
position: "absolute",
color: () => gsap.utils.random(["red", "blue", "green", "purple"])
});
gsap.from(words.words, {
duration: 2,
scale: 0,
rotation: () => gsap.utils.random(-360, 360),
x: () => gsap.utils.random(-200, 200),
y: () => gsap.utils.random(-100, 100),
stagger: {
amount: 1.5,
from: "random"
},
ease: "back.out(2)"
});
// Sequential text reveals
const textElements = gsap.utils.toArray(".reveal-line");
const tl = gsap.timeline();
textElements.forEach((element, index) => {
const split = new SplitText(element, { type: "chars" });
tl.fromTo(split.chars,
{
opacity: 0,
y: 100,
rotationX: -90
},
{
duration: 0.6,
opacity: 1,
y: 0,
rotationX: 0,
stagger: 0.02,
ease: "back.out(1.7)"
},
index * 0.8
);
});
// Text morphing with scramble
const morphScramble = gsap.timeline();
morphScramble
.to(".morph-text", {
duration: 1,
scrambleText: {
text: "",
chars: "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
revealDelay: 0
}
})
.to(".morph-text", {
duration: 1.5,
scrambleText: {
text: "NEW MESSAGE",
chars: "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
revealDelay: 0.5
}
});Best practices for text animation layouts and performance.
// Preserve layout during animations
gsap.set(".text-container", {
height: "auto",
overflow: "hidden"
});
// Fix line height issues with SplitText
const split = new SplitText(".text", { type: "lines" });
gsap.set(split.lines, {
overflow: "hidden",
height: "auto"
});
// Responsive text animations
ScrollTrigger.matchMedia({
"(min-width: 768px)": function() {
// Desktop text animation
const largeText = new SplitText(".responsive-text", { type: "chars" });
gsap.from(largeText.chars, {
duration: 1,
y: 50,
opacity: 0,
stagger: 0.02
});
},
"(max-width: 767px)": function() {
// Mobile text animation (simpler)
gsap.from(".responsive-text", {
duration: 1,
y: 30,
opacity: 0
});
}
});
// Clean up SplitText when done
const cleanup = () => {
split.revert(); // Restore original text structure
};Tips for optimizing text animations performance.
// Use CSS transforms instead of layout properties
gsap.from(splitText.chars, {
duration: 1,
y: 50, // Better than changing top/bottom
opacity: 0, // Better than visibility
force3D: true // Use hardware acceleration
});
// Batch DOM queries
const chars = gsap.utils.toArray(".char");
gsap.set(chars, { opacity: 0 }); // Set all at once
// Use will-change CSS property for elements that will animate
gsap.set(".animated-text", { willChange: "transform, opacity" });
// Clean up after animations
gsap.to(chars, {
duration: 1,
opacity: 1,
stagger: 0.02,
onComplete: () => {
gsap.set(chars, { clearProps: "all" }); // Clear inline styles
}
});