Vanilla JavaScript Integration
For static websites, CMSs, multi-page applications, or frameworks like Astro, Svelte, and Vue, you can use the standalone vanilla Scrollsequence engine. The engine coordinates scroll-driven pinning, hardware-accelerated canvas scrubbing, 3D WebGL depth maps, and dynamic HTML animations using semantic markup and data-ssq-* attributes.
1. Installation & Setup
Public Beta Installation Notice: While our official NPM registry listing is pending trademark finalization, Scrollsequence is distributed directly as a pre-packaged archive (.tgz).
Install directly via URL or download scrollsequence-2.0.0-beta.52.tgz directly into your project. Once installed, all imports (scrollsequence, scrollsequence/scrollsequence.css) work as standard!
Option A: Via NPM (ESM Bundlers)
Install scrollsequence into your project:
# Recommended: Install directly from URL
npm install scrollsequence@https://docs.scrollsequence.com/scrollsequence-2.0.0-beta.52.tgz
# Alternative: Local tarball (package.json "file:..." dependency)
# npm install ./scrollsequence-2.0.0-beta.52.tgzImport the manager and stylesheet in your application entry point:
import ScrollsequenceManager from 'scrollsequence';
import 'scrollsequence/scrollsequence.css';
// Scan the DOM and initialize all blocks, then preload media assets
ScrollsequenceManager.initialize().preloadAll();Option B: Via CDN (ESM in <script type="module">)
CDN Availability: Public jsDelivr links will activate upon general NPM registry availability. During the beta, Option A (bundler) or hosting files locally from the .tgz package is recommended.
You can load the stylesheet and ESM module directly in pure HTML without a bundler:
<!-- Scrollsequence Core Stylesheet -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/scrollsequence/dist/scrollsequence.css" />
<!-- Initialize via ESM -->
<script type="module">
import ScrollsequenceManager from 'https://cdn.jsdelivr.net/npm/scrollsequence/+esm';
ScrollsequenceManager.initialize().preloadAll();
</script>Option C: Via UMD Bundle (Global Script Tag)
For traditional script loading without ESM:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/scrollsequence/dist/scrollsequence.css" />
<script src="https://cdn.jsdelivr.net/npm/scrollsequence/dist/vanilla/free/scroll-sequence-lib-free.min.js"></script>
<script>
window.ScrollsequenceManager.initialize().preloadAll();
</script>2. Minimal HTML Example
The vanilla engine uses semantic CSS class names and data-ssq-* configuration attributes:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Pinned Scene Minimal - Scrollsequence Vanilla JS</title>
<meta name="description" content="Minimal pinned canvas scene example with overlay text using Scrollsequence in vanilla JavaScript." />
<link rel="stylesheet" href="scrollsequence/scrollsequence.css" />
<link rel="stylesheet" href="./src/style.css" />
</head>
<body class="min-h-screen bg-neutral-950 text-white">
<!-- Spacer before sequence -->
<section class="h-[50vh] flex items-center justify-center text-neutral-400">
<p class="text-lg">Scroll down to start sequence ↓</p>
</section>
<!-- Pinned Scrollsequence Scene -->
<div class="scrollsequence-scene">
<div class="scrollsequence-scene_inner">
<div class="scrollsequence-layer">
<div
class="scrollsequence-canvas2d"
data-ssq-media-groups='[{"type":"manifest","rootDir":"https://cdn.scrollsequence.com/2026-09/dna-blue/","imageSizes":{"1920":{"width":1920,"height":1080,"files":{"i":"1920/%04d.jpg"},"frameCount":293}}}]'
>
<canvas></canvas>
</div>
</div>
<div class="scrollsequence-layer">
<div class="text-center px-4">
<h1 class="text-4xl sm:text-6xl font-extrabold tracking-tight">
Cinematic Scroll!
</h1>
<p class="mt-3 text-lg sm:text-xl text-neutral-300">
60 FPS Hardware-Accelerated Scrubbing
</p>
</div>
</div>
</div>
</div>
<!-- Spacer after sequence -->
<section class="h-screen flex items-center justify-center text-neutral-400">
<p className="text-lg">Sequence ended, continue reading...</p>
</section>
<script type="module">
import ScrollsequenceManager from 'scrollsequence';
import 'scrollsequence/scrollsequence.css';
ScrollsequenceManager.initialize().preloadAll();
</script>
</body>
</html><!-- Spacer before sequence -->
<section style="height: 50vh; display: flex; align-items: center; justify-content: center;">
<p>Scroll down to start sequence ↓</p>
</section>
<!-- Pinned Scrollsequence Scene -->
<div class="scrollsequence-scene" data-ssq-duration="1500" data-ssq-scrub="1">
<div class="scrollsequence-scene_inner">
<!-- Background Canvas Layer -->
<div class="scrollsequence-layer">
<div
class="scrollsequence-canvas2d"
data-ssq-media-groups='[{"type":"manifest","rootDir":"https://cdn.scrollsequence.com/2026-09/dna-blue/","imageSizes":{"1920":{"width":1920,"height":1080,"files":{"i":"1920/%04d.jpg"},"frameCount":293}}}]'
>
<canvas></canvas>
</div>
</div>
<!-- Centered Content Overlay Layer -->
<div class="scrollsequence-layer" data-ssq-placement="center center">
<div style="text-align: center; color: white;">
<h1>Cinematic Scroll</h1>
<p>60 FPS Hardware-Accelerated Scrubbing</p>
</div>
</div>
</div>
</div>
<!-- Spacer after sequence -->
<section style="height: 100vh; display: flex; align-items: center; justify-content: center;">
<p>Sequence ended, continue reading...</p>
</section>3. DOM Hierarchy & Semantic Classes
Scrollsequence recognizes four semantic class names:
.scrollsequence-scene <-- Sticky scroll container
└── .scrollsequence-scene_inner <-- Required inner wrapper pinned by the engine
├── .scrollsequence-layer <-- 9-point spatial placement container
│ └── .scrollsequence-canvas2d <-- Canvas wrapper containing <canvas>
└── .scrollsequence-layer <-- HTML typography / UI overlay
└── <div>...</div>Crucial Requirement: Every .scrollsequence-scene element must contain a child element with the class .scrollsequence-scene_inner. The engine applies sticky pinning and clipping transforms directly to this inner wrapper.
4. Complete Data Attributes Reference (data-ssq-*)
Attributes are declared using data-ssq-{attribute} with kebab-case naming. The engine automatically converts kebab-case to camelCase and converts numbers, booleans, and JSON strings into native JavaScript types:
Scene Container (.scrollsequence-scene)
| HTML Attribute | Type | Default | Description |
|---|---|---|---|
data-ssq-duration | number | 1500 | Pin duration in pixels that the user must scroll. |
data-ssq-duration-unit | 'px' | 'px' | Unit of pin duration. Only 'px' is supported. |
data-ssq-height | number | 100 | Scene viewport height value. |
data-ssq-height-unit | string | 'vh' | Unit of scene height ('vh', 'px', '%'). |
data-ssq-top | number | 0 | Viewport offset top when pinned in pixels. |
data-ssq-top-unit | 'px' | 'px' | Unit of viewport top offset. Only 'px' is supported. |
data-ssq-trigger-start | number | 0.5 | Viewport trigger start ratio (0 = top, 0.5 = middle, 1 = bottom). |
data-ssq-trigger-end | number | 0.5 | Viewport trigger end ratio where sequence unpins. |
data-ssq-scrub | number | 1 | Smoothness factor in seconds (0 for instant snap, 1 for standard smoothing). |
data-ssq-scene-position | 'sticky' | 'flow' | 'fixed' | 'sticky' | Container positioning behavior. |
data-ssq-sticky-type | 'css' | 'js' | 'css' | Pinning mechanism: CSS sticky or JS transform-based pinning. |
data-ssq-markers | boolean | false | Visual debug markers on screen for trigger points. |
data-ssq-content-animations | JSON string | undefined | Declarative timeline animations JSON bound to scene progress. |
Layer Container (.scrollsequence-layer)
| HTML Attribute | Type | Default | Description |
|---|---|---|---|
data-ssq-placement | string | 'center center' | 9-point grid anchor: 'top left', 'top center', 'top right', 'center left', 'center center', 'center right', 'bottom left', 'bottom center', 'bottom right'. |
Canvas Wrapper (.scrollsequence-canvas2d)
| HTML Attribute | Type | Default | Description |
|---|---|---|---|
data-ssq-media-groups | JSON array | [] | Serialized array of media groups (manifests, URL lists, or gaps). |
data-ssq-width | number | 100 | Canvas width value. |
data-ssq-width-unit | string | '%' | Canvas width CSS unit ('%', 'px', 'vw'). |
data-ssq-height | number | 100 | Canvas height value. |
data-ssq-height-unit | string | 'vh' | Canvas height CSS unit ('vh', 'px', '%'). |
data-ssq-disable-depthmaps | boolean | false | Bypasses WebGL 3D depth shaders and renders 2D canvas only. |
data-ssq-depth-maps-tilt | number | 1.5 | Multiplier for 3D mouse parallax displacement intensity. |
data-ssq-depth-maps-zoom | number | 1.05 | Boundary scale zoom factor to hide outer edges during tilt. |
data-ssq-scrub | number | 1 | Smoothing scrub factor in seconds when used standalone. |
data-ssq-trigger-start | number | 0.5 | Viewport trigger start ratio when used standalone. |
data-ssq-trigger-end | number | 0.5 | Viewport trigger end ratio when used standalone. |
data-ssq-markers | boolean | false | Visual debug markers for start/end triggers. |
5. ScrollsequenceManager Lifecycle API
ScrollsequenceManager is the singleton coordinator responsible for scanning the DOM, instantiating blocks, orchestrating scroll triggers, and caching decoded image sequences.
Methods Reference
ScrollsequenceManager.initialize()
Scans the document for all .scrollsequence-scene, .scrollsequence-layer, and .scrollsequence-canvas2d elements, parses their data-ssq-* attributes, and creates active runtime blocks. Returns the manager instance for chaining.
ScrollsequenceManager.initialize();ScrollsequenceManager.preloadAll()
Initiates network streaming and browser cache decoding for all registered scenes and canvases on the page.
ScrollsequenceManager.initialize().preloadAll();ScrollsequenceManager.preloadBlock(element: HTMLElement)
Preloads media for a single element. Call this whenever you dynamically modify an element’s media groups in JavaScript:
ScrollsequenceManager.preloadBlock(canvasElement);ScrollsequenceManager.getBlock(element: HTMLElement)
Retrieves the runtime block instance (BlockScene, BlockC2d, BlockLayer) associated with a DOM element:
const sceneBlock = ScrollsequenceManager.getBlock(document.getElementById('my-scene'));ScrollsequenceManager.createBlock(element: HTMLElement, overrideAttributes?: object)
Manually instantiates or re-instantiates a block on a DOM element. If an instance already exists, it is safely destroyed and replaced. You can optionally pass programmatic attribute overrides:
ScrollsequenceManager.createBlock(sceneEl, {
duration: 2500,
scrub: 0.5,
});ScrollsequenceManager.destroyBlock(element: HTMLElement)
Properly tears down an active block instance, cancels active scroll listeners and render loops, unregisters child relationships, and releases GPU textures:
ScrollsequenceManager.destroyBlock(sceneEl);6. Programmatic Block Control (progress())
Both BlockScene and BlockC2d expose the .progress() method for 60+ FPS imperative control (e.g. from custom sliders, pointer movements, or third-party animation libraries like GSAP).
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Scene Progress Control - Scrollsequence Vanilla JS</title>
<meta name="description" content="Demonstration of manual scrubbing and programmatic progress control using Scrollsequence in vanilla JavaScript." />
<link rel="stylesheet" href="scrollsequence/scrollsequence.css" />
<link rel="stylesheet" href="./src/style.css" />
</head>
<body class="min-h-screen bg-neutral-950 text-white">
<!-- Floating Interactive Test Panel -->
<div class="fixed top-6 left-1/2 -translate-x-1/2 z-50 bg-neutral-900/95 backdrop-blur-md border border-neutral-700 rounded-2xl p-4 shadow-2xl flex flex-col gap-3 text-xs font-sans max-w-md w-[92vw] sm:w-[460px]">
<div class="flex items-center justify-between border-b border-neutral-800 pb-2">
<span class="font-semibold text-neutral-200">Scene Progress Control (Vanilla JS)</span>
<button
id="prog-reset"
class="bg-blue-600 hover:bg-blue-500 active:bg-blue-700 text-white px-3 py-1 rounded-md cursor-pointer font-medium transition-colors"
>
Reset to Scroll
</button>
</div>
<!-- Manual Scrub Slider -->
<div class="flex items-center justify-between gap-3">
<span class="text-neutral-400 font-medium">Slider:</span>
<input
type="range"
id="prog-slider"
min="0"
max="1"
step="0.001"
value="0"
aria-label="Scene progress control slider"
class="flex-1 accent-blue-500 cursor-pointer"
/>
<span id="prog-val" class="font-mono text-neutral-300 w-12 text-right">scroll</span>
</div>
</div>
<!-- Spacer before sequence -->
<section class="h-[60vh] flex flex-col items-center justify-center text-center px-4 text-neutral-400">
<h2 class="text-xl font-semibold text-white">Scene Progress Control ↓</h2>
<p class="mt-2 text-sm max-w-lg">
Try to control scene progress via slider. Can be mapped to any custom progress.
</p>
</section>
<!-- Pinned Scrollsequence Scene with Content Animations -->
<div
class="scrollsequence-scene"
id="my-scene"
data-ssq-content-animations='{"onScroll":[{"selector":".animation-attached-to-global-scene-progress","track":{"0":{"width":"calc(0% + 2px)"},"1":{"width":"calc(100% - 2px)"}}}]}'
>
<div class="scrollsequence-scene_inner">
<!-- Canvas Layer with Dual Media Groups -->
<div class="scrollsequence-layer">
<div
class="scrollsequence-canvas2d"
data-ssq-disable-depthmaps="true"
data-ssq-media-groups='[{"type":"manifest","rootDir":"https://cdn.scrollsequence.com/2026-09/dna-blue/","imageSizes":{"1920":{"width":1920,"height":1080,"files":{"i":"1920/%04d.jpg"},"frameCount":293}},"contentAnimations":{"onScroll":[{"selector":".animation-attached-to-first-media-group","track":{"0":{"opacity":0,"y":"100px","scale":0.8},"0.25":{"opacity":1,"y":"0px","scale":1},"0.75":{"opacity":1,"y":"0px","scale":1},"1":{"opacity":0,"y":"-100px","scale":0.8}}}]}},{"type":"manifest","rootDir":"https://cdn.scrollsequence.com/2026-09/dna-red/","isReverse":true,"imageSizes":{"1920":{"width":1920,"height":1080,"files":{"i":"1920/%04d.jpg"},"frameCount":293}}}]'
>
<canvas></canvas>
</div>
</div>
<!-- Global Progress Bar Layer -->
<div class="scrollsequence-layer" data-ssq-placement="top left">
<div class="w-full">
<div class="animation-attached-to-global-scene-progress h-1 bg-red-500"></div>
</div>
</div>
<!-- Dynamic Animated Text Layer -->
<div class="scrollsequence-layer" data-ssq-placement="center center">
<div class="animation-attached-to-first-media-group text-center px-4">
<h1 class="text-4xl sm:text-6xl font-extrabold tracking-tight">
Animate Any HTML Content
</h1>
<p class="mt-3 text-lg sm:text-xl text-neutral-300">
Sync typography, UI layers, and progress indicators directly to scroll
</p>
</div>
</div>
</div>
</div>
<!-- Spacer after sequence -->
<section class="h-screen flex items-center justify-center text-neutral-400">
<p class="text-lg">Sequence ended, continue reading...</p>
</section>
<script type="module">
import ScrollsequenceManager from 'scrollsequence';
import 'scrollsequence/scrollsequence.css';
ScrollsequenceManager.initialize().preloadAll();
const sceneEl = document.getElementById('my-scene');
const sceneBlock = ScrollsequenceManager.getBlock(sceneEl);
const slider = document.getElementById('prog-slider');
const valLabel = document.getElementById('prog-val');
const resetBtn = document.getElementById('prog-reset');
slider.addEventListener('input', (e) => {
const val = parseFloat(e.target.value);
sceneBlock?.progress(val);
valLabel.textContent = val.toFixed(3);
});
resetBtn.addEventListener('click', () => {
sceneBlock?.progress(null);
valLabel.textContent = 'scroll';
});
</script>
</body>
</html>Method Syntax
const sceneBlock = ScrollsequenceManager.getBlock(document.getElementById('my-scene'));
// 1. Get current progress (returns number 0.0 to 1.0)
const currentProgress = sceneBlock.progress();
// 2. Set progress imperatively (locks scroll to 50%)
sceneBlock.progress(0.5);
// 3. Release manual control back to scroll
sceneBlock.progress(null);Full Interactive Slider Example
<input type="range" id="slider" min="0" max="1" step="0.001" value="0" />
<button id="reset-btn">Reset to Scroll</button>
<div class="scrollsequence-scene" id="my-scene">
<div class="scrollsequence-scene_inner">
<div class="scrollsequence-layer">
<div class="scrollsequence-canvas2d" data-ssq-media-groups='[...]'>
<canvas></canvas>
</div>
</div>
</div>
</div>
<script type="module">
import ScrollsequenceManager from 'scrollsequence';
import 'scrollsequence/scrollsequence.css';
ScrollsequenceManager.initialize().preloadAll();
const sceneEl = document.getElementById('my-scene');
const sceneBlock = ScrollsequenceManager.getBlock(sceneEl);
const slider = document.getElementById('slider');
slider.addEventListener('input', (e) => {
sceneBlock?.progress(parseFloat(e.target.value));
});
document.getElementById('reset-btn').addEventListener('click', () => {
sceneBlock?.progress(null);
});
</script>7. JSON Configuration in HTML
Media Groups (data-ssq-media-groups)
In HTML, wrap the JSON array in single quotes (') so JSON property double quotes (") remain valid:
<div
class="scrollsequence-canvas2d"
data-ssq-media-groups='[
{
"type": "manifest",
"rootDir": "https://cdn.scrollsequence.com/2026-09/dna-blue/",
"imageSizes": {
"1920": { "width": 1920, "height": 1080, "files": { "i": "1920/%04d.jpg" }, "frameCount": 293 }
},
"autoplay": {
"fps": 30,
"repeat": 0,
"onEnter": "play",
"onEnterBack": "reverse"
}
}
]'
>
<canvas></canvas>
</div>Content Animations (data-ssq-content-animations)
Attach keyframe animations to your scene using data-ssq-content-animations:
<div
class="scrollsequence-scene"
data-ssq-content-animations='{
"onScroll": [
{
"selector": ".hero-title",
"track": {
"0": { "autoAlpha": 0, "y": "60px" },
"0.3": { "autoAlpha": 1, "y": "0px" },
"0.7": { "autoAlpha": 1, "y": "0px" },
"1": { "autoAlpha": 0, "y": "-60px" }
}
}
]
}'
>
<div class="scrollsequence-scene_inner">
<div class="scrollsequence-layer" data-ssq-placement="center center">
<h1 class="hero-title">Animated Typography</h1>
</div>
</div>
</div>Supported Animated Properties
Scrollsequence is powered by Motion under the hood. You can animate any standard CSS property or transform that Motion supports, in addition to the specialized autoAlpha macro:
- Transforms:
x,y,z,scale,scaleX,scaleY,rotate,rotateX,rotateY,rotateZ,skew,skewX,skewY(numbers or strings with units:'60px','-50%','2rem','45deg'). - Layout & Sizing:
width,height,top,left,right,bottom,margin,padding,calc(...)expressions. - Visuals & Styling:
color,backgroundColor,borderColor,borderRadius,filter('blur(8px)'),backdropFilter,clipPath,boxShadow. - Opacity: Standard CSS
opacity(0to1). autoAlpha: GSAP-style macro combiningopacityand CSSvisibility(recommended for overlay content).
Understanding autoAlpha
When fading HTML overlays in or out during scroll sequences, using opacity: 0 alone leaves transparent DOM elements interactive — they continue to capture pointer events (mouse clicks, hovers, touches) and obstruct underlying elements.
Scrollsequence provides built-in support for autoAlpha (identical to GSAP’s autoAlpha):
| Value | Opacity | Visibility | Pointer / Click State |
|---|---|---|---|
autoAlpha: 0 | 0 | 'hidden' | Non-blocking (clicks pass through) |
autoAlpha: > 0 | Animated float (0 to 1) | 'visible' | Interactive |
{
"selector": ".overlay-badge",
"track": {
"0": { "autoAlpha": 0, "y": "30px", "filter": "blur(8px)" },
"0.2": { "autoAlpha": 1, "y": "0px", "filter": "blur(0px)" },
"0.8": { "autoAlpha": 1, "y": "0px", "filter": "blur(0px)" },
"1": { "autoAlpha": 0, "y": "-30px", "filter": "blur(8px)" }
}
}Why use autoAlpha instead of opacity?
autoAlpha automatically manages visibility: hidden whenever opacity drops to 0. This guarantees that hidden text layers, cards, or buttons never block user interaction with other elements or links underneath them during scroll scrubbing.
8. Standalone Unpinned Canvas
To render an unpinned canvas in the natural document flow without a sticky scene container, place .scrollsequence-canvas2d directly inside your markup:
<section class="container">
<div
class="scrollsequence-canvas2d"
data-ssq-width="100"
data-ssq-width-unit="%"
data-ssq-height="70"
data-ssq-height-unit="vh"
data-ssq-scrub="0.8"
data-ssq-media-groups='[{"type":"manifest","rootDir":"/hero/","imageSizes":{...}}]'
>
<canvas></canvas>
</div>
</section>