@brewsite/core
v0.9.0
Published
The core animation engine for the BrewSite Scene Toolkit. Provides a TypeScript + React + Three.js framework for authoring multi-scene, scroll-driven 3D experiences via a declarative JSX DSL.
Readme
@brewsite/core
The core animation engine for the BrewSite Scene Toolkit. Provides a TypeScript + React + Three.js framework for authoring multi-scene, scroll-driven 3D experiences via a declarative JSX DSL.
Installation
npm install @brewsite/core react react-dom threePeer dependencies: react ^18 || ^19, react-dom ^18 || ^19, three ^0.183, camera-controls ^2.
Overview
Scene authors declare state in a typed JSX DSL. The compiler pre-bakes those declarations into a flat SceneTrack for O(1) runtime sampling. The widget-based runtime dispatches compiled state to registered widgets each frame — no model- or element-specific code required at the engine level.
Quick Start
Full-page marketing scroll
import {
SceneEngine, ScrollStage, BackgroundLayer, SceneCanvas, EngineOverlayHost,
InputCoordinator, corePlugin, Scene,
} from '@brewsite/core';
import { modelPlugin } from '@brewsite/model';
const PLUGINS = [corePlugin(), modelPlugin(null)];
export default function LandingPage() {
return (
<SceneEngine plugins={PLUGINS} onError={console.error}>
<Scene id="intro">...</Scene>
<Scene id="features">...</Scene>
<ScrollStage scrollHeightMode="scene-count" pixelsPerScene={1400}>
<BackgroundLayer style={{ position: 'absolute', inset: 0, zIndex: 0 }} />
<SceneCanvas style={{ width: '100%', height: '100%' }} />
<InputCoordinator />
<EngineOverlayHost />
</ScrollStage>
</SceneEngine>
);
}Embedded player (docs / inline)
import { SceneEmbed, Scene, corePlugin } from '@brewsite/core';
export function DemoWidget() {
return (
<SceneEmbed
height={400}
plugins={[corePlugin()]}
defaultTransitionDuration={500}
autoPlay
>
<Scene id="step1">...</Scene>
<Scene id="step2">...</Scene>
</SceneEmbed>
);
}Interactive 3D viewer (canvas region)
import { SceneEmbed, Scene, corePlugin } from '@brewsite/core';
export function ProductViewer() {
return (
<SceneEmbed height={500} plugins={[corePlugin()]} interactive primaryCameraId="cam">
<Scene id="viewer">...</Scene>
</SceneEmbed>
);
}Complex layout with sidebar nav
import { SceneEngine, SceneCanvas, InputCoordinator, useGoToScene, corePlugin, Scene } from '@brewsite/core';
function Sidebar() {
const goToScene = useGoToScene();
return (
<nav>
<button onClick={() => goToScene('overview')}>Overview</button>
<button onClick={() => goToScene('features')}>Features</button>
</nav>
);
}
export function DocsLayout() {
return (
<SceneEngine plugins={[corePlugin()]}>
<Scene id="overview">...</Scene>
<Scene id="features">...</Scene>
<InputCoordinator />
<div style={{ display: 'flex' }}>
<Sidebar />
<SceneCanvas style={{ flex: 1 }} />
</div>
</SceneEngine>
);
}App-level plugin hoisting (root zero-scene mode)
import { SceneEngine, SceneEmbed, Scene, corePlugin } from '@brewsite/core';
import { modelPlugin } from '@brewsite/model';
import { diagramPlugin } from '@brewsite/diagram';
// Root layout — no scenes; provides plugins for all nested embeds.
function RootLayout({ children }: { children: React.ReactNode }) {
const plugins = useMemo(() => [corePlugin(), modelPlugin({ manifestUrl: '/manifest.json' }), diagramPlugin()], []);
return (
<SceneEngine plugins={plugins}>
{children}
</SceneEngine>
);
}
// Anywhere nested (plugins inherited automatically):
function ProductPage() {
return (
<SceneEmbed height={400} autoPlay> {/* no plugins prop needed */}
<Scene id="hero">...</Scene>
</SceneEmbed>
);
}Key Exports
Core Engine
| Export | Description |
|---|---|
| SceneEngine | Root engine component — pure context provider, zero DOM output |
| SceneEmbed | Self-contained embedded player with auto-play, controlled progress, visibility lifecycle, and interactive camera |
Layout Components
| Export | Description |
|---|---|
| ScrollStage | DOM layout helper for the full-page sticky-canvas pattern |
| BackgroundLayer | Wires engine.setBackgroundRef to a positioned div |
| SceneCanvas | Renders the Three.js canvas |
| EngineOverlayHost | Renders HUD overlay content |
| EngineARContainer | Fixed aspect-ratio container with scale mode handling |
| EngineGate | Gates rendering until the first frame; shows placeholder during loading |
Input Components
| Export | Description |
|---|---|
| InputCoordinator | Unified input handler for scroll, keyboard, and pointer input |
Hooks
| Export | Description |
|---|---|
| useEngineState() | Engine frame state from nearest SceneEngine context |
| useEngineState(id) | Engine frame state from global registry by engine id |
| useGoToScene() | Returns a stable function for programmatic scene navigation |
| useEngineScrubber() | Scrubbing state + setProgress for drag/seek UI |
| useSceneProgress() | Current scene-local progress [0, 1] |
| useCurrentScene() | Current scene id and index |
| useSceneRuntime(id) | Runtime state (assets, viewport) from global registry |
| useSceneEngineContext() | Raw engine context for advanced custom integrations |
| useVisibilityGate(ref, mode) | Viewport-aware mount/pause lifecycle hook (beta) |
| useNativeScrollSource(opts) | Hidden native scroll region as IScrollSource |
Plugin System
| Export | Description |
|---|---|
| corePlugin | Registers core widgets: Lighting, Background, Environment, Floor, Camera, SceneMeta |
Compiler DSL (scene authoring)
| Export | Description |
|---|---|
| Scene | Root DSL component for a scene frame |
| Hud / HudItem | HUD overlay authoring |
| InputController / Action | Action-mapped input configuration |
| PointerMap / WheelMap / KeyMap / PinchMap | Input binding maps |
| registerNode | Register a custom DSL node handler |
Widget SDK
| Export | Description |
|---|---|
| WidgetRegistry | Plugin registry — maps DSL components to widget instances |
| VariableStore | Reactive key-value store for cross-widget state |
| useVariable | React hook for reading a VariableStore variable |
| CUSTOM_NODE_HANDLER | Symbol for widgets that override default DSL node routing |
UI Components
| Export | Description |
|---|---|
| TimelineWidget | Timeline scrubber overlay component |
| CameraControlPanel | Camera orbit/dolly control UI (dev tool) |
| SceneInspector | Scene navigation overlay (dev tool) |
Upgrading from v1
See MIGRATION.md for a complete v1 → v2 upgrade guide.
Summary of breaking changes:
SceneEnginereplaces the deprecatedEngineProviderEngineInputRegiondeleted → useScrollStage(scroll mode) orSceneEmbed(embedded mode)ScrollCaptureSectiondeleted → useScrollStageuseEngineScroll/useEngineInputdeleted → functionality internalized in input componentsuseSceneEngineState(id)deleted → useuseEngineState(id)engine.scrollToProgress(p)deleted → useengine.setProgress(p)oruseGoToScene()InputModePolicy/ScrollSourcetypes deleted → no replacement neededuseEngineScrubberno longer takes an options argument
Architecture
The engine is structured in layers (top to bottom):
- Player (
src/player/) — React integration surface - Runtime (
src/runtime/) — Generic widget-based execution coordinator - Compiler (
src/compiler/) — Pure DSL → SceneTrack pipeline (no Three.js, no React) - Elements (
src/elements/) — Core renderable widgets (model, camera, lighting, background, environment, floor) - Widget SDK (
src/widget/) —WidgetRegistry,VariableStore, widget interfaces - HUD / Input — Overlay and input systems
Each element follows a strict module pattern:
types.ts → dsl.tsx → compile.ts → render.ts → {Name}Widget.ts → index.tsPeer Dependencies
| Package | Version |
|---|---|
| react | ^19 |
| react-dom | ^19 |
| three | ^0.183 |
License
See LICENSE.
