
cursor-swarm.js is a dependency-free JavaScript library that animates cursors building a web page as visitors scroll.
It draws boxes, types text, and reveals media on existing pages, with an optional reverse animation when visitors scroll up.
Features
- 5 build animations for boxes, controls, headings, body text, and media.
- Agent cursors share layout, typing, and media tasks.
- A progress HUD, cursor labels, guide lines, and completion particles.
- Optional copy-paste animations for repeated cards and UI elements.
- Optional relocation animations that move elements into position.
- Adaptive animation speed for rapid scrolling.
- Per-element animation overrides through HTML attributes.
- Built-in support for reduced-motion preferences.
How To Use It
Basic Usage
Place the core JavaScript before the closing body tag. By default, CursorSwarm scans document.body and animates elements as they enter the viewport.
<script src="cursor-swarm.js"></script>
<script>
const swarm = new CursorSwarm({ cursors: 3 }).init();
</script>
Animate Only One Section
Set root to a section selector to animate part of the page. Add data-cs-ignore to elements that should be excluded.
<section id="feature-showcase">
<h2>Build better workflows</h2>
<p>Organize projects and share updates from one workspace.</p>
<div style="padding: 24px; background: #f1f5f9; border: 1px solid #cbd5e1;">
<h3>Project overview</h3>
<p>Track the work that matters.</p>
</div>
<button type="button" data-cs-ignore>Keep this control static</button>
</section>
<script src="cursor-swarm.js"></script>
<script>
const swarm = new CursorSwarm({
root: '#feature-showcase',
cursors: 3,
palette: 'ocean',
deconstruct: false
}).init();
</script>
Initialize With HTML Attributes
For automatic initialization, load cursor-swarm. js in <head> with data-auto. The script hides the page until the cursors are prepared and exposes the instance as window.cursorSwarm.
<script src="cursor-swarm.js"
data-auto
data-cursors="4"
data-palette="neon"
data-start-position="corners"
data-exit-after-build="home"></script>
For manual initialization from <head>, call CursorSwarm.cloak() immediately and initialize on DOMContentLoaded to prevent the original content from flashing:
<script src="cursor-swarm.js"></script>
<script>
CursorSwarm.cloak();
document.addEventListener('DOMContentLoaded', () => {
window.cursorSwarm = new CursorSwarm().init();
});
</script>
Customize Cursor Colors and Movement
CursorSwarm includes default, neon, pastel, sunset, ocean, forest, candy, mono, and ink palettes. You can register a palette to use your own colors. copyPaste and relocation accept true or a probability from 0 to 1.
CursorSwarm.palettes.cssscript = {
colors: ['#2a6bf2', '#f25a72', '#16a085']
};
const swarm = new CursorSwarm({
cursors: 3,
palette: 'cssscript',
startPosition: 'outside',
copyPaste: 0.5,
relocation: 0.3,
exitAfterBuild: 'home'
}).init();
Control Build Types in HTML
CursorSwarm assigns a build type to each HTML element. Override it with data-cs. For media, set data-cs-dir to l, r, or t to control the scan direction. data-cs-copy and data-cs-relocate override the corresponding global options.
| Build type | Animation |
|---|---|
frame | Draws the outline of a box before its background and border appear. |
pop | Clicks controls and small items into view. |
type | Types headings and other short text with a caret. |
words | Reveals paragraphs and list text word by word. |
media | Reveals images and other media with a scanning wipe. |
<h2 data-cs="type">A heading built by cursors</h2> <p data-cs="words" data-cs-label="intro">This paragraph is revealed one word at a time.</p> <img src="preview.jpg" alt="Product preview" data-cs="media" data-cs-dir="t"> <div data-cs="none"><p>Animate the children, not this container.</p></div> <div data-cs="skip">Leave this element and its children unchanged.</div>
All Configuration Options
Pass an options object to new CursorSwarm(options) to configure the animation.
Cursors and Scope
| Option | Description |
|---|---|
cursors | 3. Number of cursors when agents is not provided. |
agents | null. Array of agent objects with name, color, role (layout, type, or media), x, and y. A supplied array determines the cursor count. |
names | null. Agent name array or a function of the agent index. |
palette | 'default'. A preset name, 'mono:#ff5e00', a CSS color, an array or comma-separated color list, or { colors, mono, hud }. The optional hud object accepts background, color, mute, and border. |
colors | null. Explicit cursor colors, cycled across agents and taking precedence over palette colors. |
startPosition | 'center'. center, random, corners, top, bottom, left, right, outside, {x,y}, an array of points, or (i,n) => ({x,y}). Coordinates accept pixels or percentage strings. |
root | null (document.body). Element or selector containing the content to animate. |
targets | null. Selector restricting animation to matching elements. |
ignore | null. Selector for elements to leave unchanged. |
Animation Behavior
| Option | Description |
|---|---|
frames | true. Draw frames around eligible boxes. |
deconstruct | true. Reverse completed builds when scrolling upward. |
exitAfterBuild | false. true or 'edge' moves idle cursors offscreen; 'home' returns them to their starting points. |
idle | 'dim'. Idle cursor style: dim, stay, or hide. |
spawn | true. Animate cursors when they first appear. |
speed | 1. Global speed multiplier. |
adaptiveSpeed | true. Increase speed as the pending work grows. |
maxSpeed | 4. Maximum multiplier applied by adaptive speed. |
trigger | 0.88. Viewport fraction at which an element becomes eligible for building. |
typeDuration | 1.6. Maximum typing duration per text block, in seconds. |
copyPaste | false. true uses a 50% chance or specify a probability from 0 to 1. |
relocation | false. true uses a 30% chance or specify a probability from 0 to 1. |
respectReducedMotion | true. Skip the effect when prefers-reduced-motion: reduce is active. |
Cursor Overlays and Callbacks
| Option | Description |
|---|---|
hud | true. Show the current task and build progress. |
hudPosition | 'bottom-left'. Also accepts bottom-right, top-left, or top-right. |
labels | true. Show agent names beside cursors. |
marquee | true. Show selection outlines during building. |
guides | true. Show crosshair guides. |
sparks | true. Show completion particles. |
caret | true. Show a blinking typing caret. |
zIndex | 2147483000. Stacking level of the cursor overlay. |
onBuild | null. (el, type, agentName) callback after an element is built. |
onUnbuild | null. (el, type, agentName) callback after an element is deconstructed. |
onIdle | null. (agent) callback when an agent becomes idle. |
onDone | null. () callback when all registered elements are built. |
Public Methods and Properties
| Method | Description |
|---|---|
init() | Scan content, create the cursors, and start building; returns the instance. |
pause() | Pause cursor movement and ongoing animation. |
resume() | Resume a paused swarm. |
toggle() | Switch between paused and running states. |
stop({ reveal: true }) | Stop and reveal pending elements by default; reveal: false disables the instant reveal. |
destroy() | Stop, remove the overlay, and restore the original text nodes and affected inline styles. |
buildAll() | Show all registered content immediately and retain the instance for subsequent work. |
refresh() | Scan the root for new elements and register them for animation. |
add(el, type?) | Register an element or selector, optionally specifying a build type. |
setSpeed(n) | Set the speed multiplier while running. |
setPalette(p) | Change cursor and HUD colors using a preset or palette value. |
on(event, fn) | Subscribe to an instance lifecycle event. |
off(event, fn) | Unsubscribe a previously registered handler. |
| Property | Description |
|---|---|
state | Current state: idle, running, paused, stopped, or destroyed. |
progress | Fraction of registered items built, from 0 to 1. |
colors | Array of current agent colors. |
agents | Current agent objects. |
records | Registered element records. |
Static API
| Member | Description |
|---|---|
CursorSwarm.palettes | Built-in palette definitions; add a custom preset with a new key. |
CursorSwarm.mono(color, count) | Generate shades from a base color. |
CursorSwarm.inkOn(color) | Choose black or white based on the supplied background color. |
CursorSwarm.cloak() | Hide the page until initialization prepares its animations. |
Events
Register listeners before calling init() to capture the initial start event.
| Event | Arguments and timing |
|---|---|
start | Fires after initialization; no arguments. |
build | (el, type, agentName) when an element is built. |
unbuild | (el, type, agentName) when an element is deconstructed. |
idle | (agent) when a cursor becomes idle. |
done | Fires when all registered elements have been built; no arguments. |
pause | Fires on pause; no arguments. |
resume | Fires on resume; no arguments. |
stop | Fires when the swarm stops; no arguments. |
destroy | Fires when the instance is destroyed; no arguments. |
palette | (palette) when setPalette() updates the palette. |
const swarm = new CursorSwarm({ root: '#feature-showcase' });
swarm.on('build', (element, type, agentName) => {
console.log(agentName, type, element);
});
swarm.on('done', () => {
console.log('Page build complete');
});
swarm.init();
HTML Attribute Supported
| Attribute | Description |
|---|---|
data-auto | Automatically initialize when added to the script element. |
data-* on the script | Set supported options in kebab-case. Numeric/boolean strings are parsed, and colors/names accept comma-separated lists. Use JavaScript for object and function values. |
data-cs-ignore | Exclude this element and its descendants. |
data-cs="skip" | Exclude this element and its descendants. |
data-cs="none" | Leave the element itself unchanged, but build eligible children. |
data-cs="frame/pop/type/words/media" | Force one of the five build types. |
data-cs-dir | Media scan direction: l, r, or t. |
data-cs-label | Custom text shown for the element in the HUD. |
data-cs-copy | true or false to force or prevent copy-paste when eligible. |
data-cs-relocate | true or false to force or prevent relocation when eligible. |
Alternatives and Related Resources
- 25+ CSS Scroll/Reveal/Loading/Typing Animations for the Web – CSS Animation
- Performance-First Scroll Animation Library – WavBlock
- Text Reveal Animations On Scroll – Textify.js







