Statify extensions
Scene widget
Use the Scene client widget to place an interactive Three.js scene in a
Statify page. A scene can load glTF/GLB models and HDR environments, expose
named views, discover cameras and animations stored in a GLB, and switch
between several arrangements of the same resources.
Start with the game-3d template
For a new 3D site, initialize Statify's game-3d template:
statify init my-game-site game-3d
cd my-game-site
npm start
The game-3d template already includes the extra Scene JavaScript and CSS
libraries. Authors using that template only need to define the widget in page
JSON and add their scene assets.
When adding Scene to another template, copy the compiled Scene files into the
site and load them once from metadata.json. Load the Scene script after
Statify's client runtime:
{
"head": [
{"t": "link", "rel": "stylesheet", "href": "/app/extra/statify-scene.css"},
{"t": "script", "src": "/app/website.min.js"},
{"t": "script", "src": "/app/extra/statify-scene.min.js"}
]
}
Scene structure
The object passed to attrs is the scene definition itself.
{
"res": {},
"env": {},
"cam": {},
"run": "showroom",
"scenes": {},
"debug": false
}
| Field | Purpose |
|---|---|
res |
Named GLB, glTF, and HDR resources. |
env |
Background, environment map, exposure, fog, ground, and light. |
cam |
Named interactive cameras and their saved spots. |
run |
ID of the scene selected when the widget starts. |
scenes |
Named combinations of visible resources and available cameras. |
debug |
Shows the temporary authoring control used to capture camera spots. |
All declared resources are loaded before the scene starts. Keep files suitable for the web even when the source game uses higher-resolution assets.
Resources
Declare every asset once under res, then refer to it by its ID elsewhere:
{
"res": {
"level": {"file": "/res/scene/level.glb"},
"sky": {"file": "/res/scene/studio.hdr"}
}
}
Supported model formats are .glb and .gltf; environment maps use .hdr.
A model remains hidden until the active named scene includes its resource ID in
show.
Use autoplay to start one or more animation clips as soon as a model is
shown. A value may be an animation ID or label, or an array of them:
{
"res": {
"character": {
"file": "/res/scene/character.glb",
"autoplay": ["Idle", "Blink"]
}
}
}
Animations discovered in visible models are also exposed in the widget's animation row. Each animation button toggles that clip on and off.
Environment, ground, and shadows
env controls the shared Three.js environment:
{
"env": {
"envMap": "sky",
"backgroundColor": "#070711",
"exposure": 1,
"fog": false,
"fogColor": "#070711",
"fogDist": 0.01,
"ground": {
"position": [0, 0, 0],
"size": [50, 50],
"color": "#171725",
"reflect": 0.1
},
"light": {
"pos": [20, 30, 10],
"target": [0, 0, 0],
"color": "#ffffff",
"intensity": 1.5,
"shadow": {
"cameraSize": 80,
"near": 0.1,
"far": 150,
"bias": -0.0001,
"normalBias": 0.02
}
}
}
}
envMap names an HDR resource and applies it as both the lighting environment
and the background. When no environment map is selected, backgroundColor
remains visible.
The ground is optional. When used, ground.position creates it, size sets
its width and depth, and reflect is a value from 0 to 1. Omit ground
when the model already contains its terrain, floor, or complete surroundings.
light creates a directional light. Adding a shadow object enables shadow
rendering, makes model meshes cast shadows, and lets the ground receive them.
cameraSize controls the total area covered by the light's orthographic shadow
camera; increase it for large levels, or reduce it for more detailed shadows in
a smaller area.
Cameras and spots
Declare cameras under cam. The supported authored camera types are orbit,
walk, and follow.
{
"cam": {
"overview": {
"label": "Overview",
"type": "orbit",
"default": "entrance",
"position": [8, 5, 12],
"target": [0, 2, 0],
"minDistance": 2,
"maxDistance": 40,
"spots": [
{
"id": "entrance",
"label": "Entrance",
"position": [8, 5, 12],
"target": [0, 2, 0]
},
{
"id": "hangar",
"label": "Hangar",
"position": [-10, 4, 3],
"rotation": [0, -90, 0]
}
]
},
"visitor": {
"label": "Walk",
"type": "walk",
"position": [0, 1.7, 8],
"speed": 4,
"lookSpeed": 0.002
},
"follow-ship": {
"label": "Follow Ship",
"type": "follow",
"target": "level/Ship",
"offset": [0, 4, 10]
}
}
}
Camera poses accept position or pos. Orientation can use Euler degrees in
rotation, or [x, y, z, w] values in quaternion or quat. Orbit cameras
can also declare target, dampingFactor, rotateSpeed, panSpeed,
zoomSpeed, moveSpeed, and lookSpeed.
A follow target may be an object name or resourceId/ObjectName. Qualify the
name when two resources contain objects with the same name.
Any cameras contained in a visible GLB are discovered automatically and added
to the available cameras. Their IDs are namespaced as
resourceId/cameraName. Animations contained in the GLB are discovered at the
same time.
The default desktop controls are:
- left drag rotates around the selected geometry;
- middle drag pans;
- right drag looks freely;
- the mouse wheel zooms;
W,A,S, andDmove,QandEmove vertically, and Shift moves faster while free-looking.
Touch input supports one-finger rotation and two-finger zoom and pan.
Named scenes
A named scene selects which resources are visible and which authored cameras belong in its interface:
{
"run": "showroom",
"scenes": {
"showroom": {
"label": "Showroom",
"show": ["level"],
"cams": ["overview", "visitor"],
"camera": "overview"
},
"flight": {
"label": "Flight",
"show": ["level"],
"cams": ["follow-ship"],
"camera": "follow-ship",
"html": "<strong>Flight demonstration</strong>"
}
}
}
show contains resource IDs. cams contains authored camera IDs, while
camera is the initial camera for that named scene. Cameras discovered inside
the visible GLB resources are appended automatically. Optional html is
trusted authored HTML displayed over the scene; never populate it from
untrusted input.
The widget renders selectors only when they are useful: scenes select the active arrangement, cameras select the current viewpoint, spots select saved positions for the active camera, and animations toggle imported clips.
Creating a Scene page
A dedicated Scene page uses Statify's Client page widget. Place the scene
definition directly in client.attrs:
{
"widget": "Client",
"title": "Explore the level",
"client": {
"widget": "Scene",
"attrs": {
"res": {
"level": {"file": "/res/scene/level.glb"},
"sky": {"file": "/res/scene/studio.hdr"}
},
"env": {
"envMap": "sky",
"exposure": 1,
"light": {
"pos": [20, 30, 10],
"target": [0, 0, 0],
"intensity": 1.5
}
},
"cam": {
"overview": {
"label": "Overview",
"type": "orbit",
"position": [8, 5, 12],
"target": [0, 2, 0]
}
},
"run": "showroom",
"scenes": {
"showroom": {
"label": "Showroom",
"show": ["level"],
"cams": ["overview"],
"camera": "overview"
}
}
}
}
}
Embedding Scene in a FeatureList
Client widgets can also be placed directly in a FeatureList items array. In
this form, use widget: "Scene" and put the same scene definition in attrs.
Do not add the outer Client page wrapper:
{
"widget": "FeatureList",
"title": "Interactive tour",
"list": [
{
"title": "Explore the ship",
"containerClass": "scene-feature",
"items": [
{
"widget": "Scene",
"attrs": {
"res": {
"ship": {"file": "/res/scene/ship.glb"},
"sky": {"file": "/res/scene/studio.hdr"}
},
"env": {"envMap": "sky", "exposure": 1},
"cam": {
"exterior": {
"label": "Exterior",
"type": "orbit",
"position": [12, 5, 18],
"target": [0, 2, 0]
}
},
"run": "ship",
"scenes": {
"ship": {
"label": "Ship",
"show": ["ship"],
"cams": ["exterior"],
"camera": "exterior"
}
}
}
}
]
}
]
}
The same client-widget item can be used inside the nested items of a feature
or card. Keep the scene definition unchanged; use the surrounding FeatureList
classes to control its placement.
Give an embedded scene a definite usable height or aspect ratio in the site's
CSS. Do not repeatedly derive a parent height from the canvas's changing
rendered height, because that can create a ResizeObserver feedback loop.
Each Scene instance creates its own WebGL renderer and loads its own resources. Prefer one Scene with several named scenes over a grid of heavy independent viewers, and optimize GLB and HDR assets before publishing.
Authoring and validation
Set debug: true temporarily to display the + control. It asks for a name
and captures the current camera pose as an in-memory spot. Copy the resulting
pose into the appropriate camera's spots array; debug changes are not a
replacement for editing the source JSON.
Before publishing:
- Verify that every ID used by
envMap,show,cams,camera,run, and follow targets exists. - Test every scene, camera, spot, and animation control.
- Test mouse, keyboard, touch, and resize behavior.
- Check the page on a mobile device and on a machine with a modest GPU.
- Confirm that models and HDR files are appropriately compressed for web delivery.