isostate Get started →

Controller Scroll

Start with mountScene and opt into the runtime controller when the scene should follow a scroll container.

import { mountScene } from '@sebastianwessel/isostate';
import sceneBundle from './scene.isostate.js';

const target = document.querySelector<HTMLElement>('#scene');
const scroller = document.querySelector<HTMLElement>('#story');

if (!target || !scroller) {
	throw new Error('Missing scene or scroll container');
}

const mounted = mountScene(target, sceneBundle, {
	label: 'Scroll-driven infrastructure scene',
	controller: {
		container: scroller,
		scrollDirection: 'vertical',
		scrollOffset: { top: 120, bottom: 240 },
		minProgress: 0,
		maxProgress: 1,
		keyboardControls: false,
		touchControls: false
	}
});

mounted.controller?.on('progress-change', (progress) => {
	console.log('scene progress', progress);
});

window.addEventListener('pagehide', () => mounted.destroy(), { once: true });

The controller batches scroll updates with requestAnimationFrame, clamps progress to the configured range, and forwards the active progress to the animation engine.

Browser visual and performance checks are opt-in:

ISOSTATE_BROWSER_TESTS=1 bun test tests/browser