npm install @caustics/spatial-scroll <script> import { SpatialScroll } from '@caustics/spatial-scroll'; </script> <SpatialScroll preset="orbit" />
npm install @caustics/spatial-scroll import { SpatialScroll } from '@caustics/spatial-scroll/react'; return <SpatialScroll preset="orbit" />;
npm install @caustics/spatial-scroll import { mount } from '@caustics/spatial-scroll'; mount(document.querySelector('#hero'), { preset: 'orbit' });
| Prop | Type | Default | Description |
|---|---|---|---|
| preset | string | "orbit" | Named preset. Overrides individual props when set. |
| colors | string[] | undefined | Override palette. Accepts hex, hsl, or CSS custom properties. |
| reducedMotion | 'pause' | 'static' | 'pause' | Behavior when prefers-reduced-motion is active. |
| class | string | undefined | Additional CSS classes applied to the root element. |
caustics-spatial-scroll/ ├── svelte/ SpatialScroll.svelte · index.ts · types.ts ├── react/ SpatialScroll.tsx · index.ts · types.ts ├── vanilla/ spatial-scroll.ts · compiled .js ├── docs/ README.md · API.md · CHANGELOG.md ├── LICENSE.md Caustics Commercial License └── package.json
Spatial Scroll is the 3D line’s flagship — the scroll-bound 3D reveal you’ve seen on flagship product pages, expressed as a declarative keyframe timeline instead of a hand-wired animation rig. You give it a GLB and a sequence of camera keyframes (or a named preset); it binds them to scroll progress and renders a quaternion-correct camera path, frame-rate-independent, with HTML captions that track 3D anchors.
It is the catalog’s first component to ship a 3D renderer, and it does so without bloating the bundle. Rather than pull in three.js (~160 KB), it owns a trimmed OGL renderer and its own GLB loader, lazy-loaded as a code-split chunk — so a page that scrolls past the section pays a few KB, and the renderer only loads when the model does. Honest, gzipped: a ~0.4 KB headline entry plus a ~2.7 KB engine and the ~19 KB lazy OGL renderer, around 22 KB per page for a plain GLB. The owned loader decodes glTF accessors, flattens the node hierarchy to world matrices, and reads embedded textures; a malformed or compressed (DRACO / KTX2) GLB throws a clear error and the engine shows your poster instead of a blank canvas.
The hard parts are handled. Camera interpolation is a shortest-arc quaternion slerp with an nlerp fallback near parallel — no gimbal flips at the keyframe seams. The scrub is frame-rate-independent (60 Hz and 144 Hz land identically). The mobile pin is computed from the ratio of measured rects, never window.innerHeight, so the iOS address-bar resize doesn’t make the pinned section jump. Captions project from 3D to screen with a behind-camera clip test. It honors the full Caustics lifecycle — IntersectionObserver and Page-Visibility gating, WebGL context-loss/restore from the cached model, reduced-motion (a held final frame), DPR capped at 2, a poster fallback when WebGL is unavailable, and an idempotent destroy(). Ships for Svelte 5, React 18+, and vanilla JS through one factory.
Caustics Commercial License · lifetime updates within v1