Animation: insert keyframe at the playhead — transform or Material track for the selected object; audio volume when a timeline audio clip is selected (pick the clip in Graph → Volume or Tracks → Audio)
Alt+I
Animation: delete keyframe at the playhead
Space
Animation: play / pause the Timeline
Numpad 0
Look through the selected (or first) scene camera — toggle to exit. Orbit / pan / zoom moves that camera (Lock Camera to View). Resolution menu sets the passepartout frame
Ctrl+Z
Undo last scene change (gizmo / Transform panel including Freeze & Reset Pivot, group/ungroup) · in Splat Edit: undo splat selection/delete
Ctrl/Cmd+N / O / S / Shift+S
File shortcuts: New · Open · Save · Save As
Ctrl+C / V / D
Copy / Paste / Duplicate the selected node
Ctrl+G / Ctrl+Shift+G
Group / Ungroup (multi-select supported for mesh objects)
Splat Edit: subtract from selection while dragging
Shift+LMB
Splat Edit: invert selection in the box / lasso / brush stroke (selected ↔ unselected inside the region)
Alt hold
Splat Edit: orbit / pan without leaving edit mode
[ / ]
Splat Edit: decrease / increase brush size
File menu
New
Clear the scene and start empty — closes all open Windows-menu panels (Tools Window, Timeline, 3DGS Simplify Tool, Scene Analyze, Log, Performance, AR Experience)
Open…
Replace the scene with a scene zip from this app (named from the top-bar project: {name}.zip with {name}.glb + {name}.js and sidecar media; older *_scene.zip still opens), a Scene GLB, mesh GLB / glTF, mesh FBX (.fbx — static meshes, skinning, joints, textures, animation), or splat (.ply / .splat / .ksplat / .spz / .sog). Drag-drop onto the viewport uses the same formats. With 3DGS Simplify Tool open, splat Open/Import goes into that tool’s Original preview instead. Same window close behavior as New
Save
Quick-save Scene (zip) — same package as before ({name}.zip with {name}.glb + {name}.js, lights, HDR, shaders, sidecar media). Overwrites the zip you opened or last saved when the browser allows writing that file; otherwise opens the native Save as picker on the same click
Save As…
Save Scene zip to a new name or folder (native Save as when supported; download fallback otherwise)
Save Selection…
Opens the resizable save window for the selected splat or mesh — pick format, then confirm the file name. Optional Overwrite when saving back to a known zip stem. Formats that do not apply are greyed out. Mesh selection can export .glb, embedded .gltf, a zip with separate glTF textures, or binary FBX (.fbx) — when textures are connected, FBX downloads as a zip with the .fbx, textures/ PNG sidecars (slot suffixes in filenames), and a {name}_texture_links.txt relink guide; UV V is flipped for typical DCC import. Splat format conversion works on static web hosts (in-browser) as well as with python run.py. Drag the title to move; edges/corners to resize (size remembered)
Import…
Add another splat or mesh (GLB, glTF, or FBX) without replacing the scene. FBX import uses the same mesh / skin / animation pipeline as GLB. With Simplify Tool open, splat files load into Original
Export as HTML zip…
Downloads {name}.zip from the top-bar project name — a flat zip (no extra folder) with the offline viewer HTML plus sidecar media (see HTML export zip below). Includes meshes, materials (Standard / Phong / Basic / Movie / Shadow Catcher / Occlusion), Outliner lights & shadows, studio/HDR lighting, sky/background, viewport post-processing, and splats. When splats are present, a Splat file format dialog runs first (same choices as AR export)
Export AR zip…
Builds a mobile AR package ({name}-ar.zip) with index.html, scene.glb, scene.json (includes Default / On-start, touch gesture Actions, and LINK BUTTON HUD when authored), splat sidecars, Movie video sidecars, and bundled runtime. When splats are present, pick their export format in the dialog before packing. When a Movie uses a non-web-safe video (e.g. iPhone .mov), a second dialog offers Optimize movies for web (H.264 MP4) (on by default). Same layout and relative scale as Preview 3D in the AR panel. Requires HTTPS to host. See Web AR overview / World placement AR / Image-target AR help tabs
Save file confirm
When it appears
After you pick a format in Save Selection, or when an export finishes packing (mesh/splat download, Export as HTML zip, Export AR zip, screen capture, and similar writes). File → Save for Scene zip skips this step when overwriting an opened zip; it uses the native picker directly when a new path is needed
File name
Edit the suggested name; invalid characters are replaced. If you remove the extension, the original suffix is restored (including .compressed.ply). Enter or Save… continues; Cancel aborts the write
Save…
Opens the browser’s native Save as dialog when supported (pick folder + name). Otherwise the file downloads with the confirmed name — use the browser’s “Ask where to save each file” setting to choose a folder on each download
HTML export zip
Package
Zip is flat (no extra folder): {name}.html plus sidecar files next to it. Name comes from the top-bar project
Meshes
All meshes pack into one GLB named from the top-bar project ({name}.glb). Model textures (albedo, normal, roughness, alpha, …) are embedded in that GLB — no separate texture files. The viewer loads it once and places each mesh by name
Lighting / sky
Mesh IBL HDR/EXR is a separate file. Background photo or LDR sky is a separate image. When env and background share the same HDR, only one HDR is written (plus an LDR sky.jpg plate for reliable display)
Splats
When the scene includes splats, the exporter asks for a sidecar format (.compressed.ply default, or .ply / .sog / .spz / .splat / .ksplat / splat .glb). The viewer uses the same Spark path as the editor so silhouettes stay tight. Meshes render underneath; Shadow Catcher shadows depth-test against splats and draw on top
Post-processing
Tools Window → Scene Settings → Post-Processing Filters (Bloom, FXAA, depth of field, pixelation, color grading / LUT) export with the scene and apply in the offline HTML viewer
Movies
Video files stay as separate files next to the HTML (not inlined). Movie materials play video + audio; if the browser blocks unmuted autoplay, the first click or keypress unlocks sound
Open / share
Fully unzip — do not open the HTML from inside the zip. Keep {name}.html, {name}.glb, HDR/sky/background, splat sidecars, and any movie files together. Model textures are already inside the GLB. Open the .html from that folder (double-click or a local static server)
Edit menu
Undo
Undo the last scene change — transform (gizmo / Model Info, including Freeze Transform & Reset Pivot to World) or group/ungroup. In Splat Edit, undoes splat selection/delete (Ctrl+Z)
Copy
Copy the selected splat, glTF object, or mesh part
Paste
Paste from the editor clipboard (slightly offset)
Duplicate
Copy and paste the selected node in one step (not a Group)
Delete
Remove the selected object, light, or (in Splat Edit) selected gaussians
Group
Wrap selected mesh objects, nest splats and/or whole meshes under a shared parent Group (children inherit its transform). Shift/Ctrl+click to multi-select. Objects are not auto-grouped on open
Ungroup
Dissolve the selected group(s) — mesh Group, splat parent Group, or splat virtual group
Freeze Transform
Zero Position / Rotation / Scale without moving the object — bakes the current pose into the geometry / hierarchy. Works for splats, meshes, nodes, and Group folders
Reset Pivot to World
Move only the selected model's pivot to the world / grid center (0,0,0). Clears Position to 0; rotation and scale stay; the object stays put
Edit Mode menu
Layout
Default — select and transform in the Outliner / viewport; no edit toolbars
Splat Edit
Toolbar appears below the top bar — box / lasso / brush select, delete floaters, undo. Shift+LMB inverts splats inside the stroke; Ctrl/Cmd subtracts. Scroll zooms the viewport (ortho axis views included). Hold Alt to orbit without leaving edit mode
Object Edit
Toolbar below the top bar — add Primitives (box, sphere, …) and delete mesh objects
Lighting Edit
Toolbar below the top bar — Add Light (Directional / Point / Ambient / Hemisphere) under Scene → light_grp. Click a light helper or Outliner row to select. Directional: amber orbit = az/el (aims at world origin), cyan = distance. Point / hemisphere use the normal gizmo; ambient has no transform. Color, power, Cast shadow, Softness (0–200), Quality (Low–Ultra), and aim live in Model Info → Selected Light (drag ⠿ to reorder sections)
Animation Edit
Toolbar below the top bar — Add Render Cam, insert keyframes, Auto Key vs Manual, Camera View (Numpad 0), camera resolution (passepartout), and Timeline. In Camera View, orbit/pan/zoom moves the camera (Lock Camera to View). Keyframe any Outliner node except the Scene root (meshes, joints, groups, lights, splats, cameras). Interpolation: Auto Smooth / Linear / Constant / Ease
Top bar icons
Grid
Toggle world grid and axes at the origin. Line colors follow the light / dark UI theme (RGB axis colors unchanged). Sized to the scene; one cell = default transform-handle length. In ortho views the grid plane aligns to the viewport (axes stay world XYZ)
Performance
Speedometer icon — open the monitor or toggle the compact HUD. When closed, opens the full window; when the full window is open, minimizes to the HUD; when the HUD is showing, restores the full window
Viewport
Camera — Perspective (free orbit), or ortho Front / Back / Right / Left / Top / Bottom (pan & scroll zoom; orbit locked). Scroll wheel zooms in Layout and all edit modes. Shortcuts like Blender: 1/3/7/5; opposite with Ctrl+Numpad or Shift+number; 9 flips. Returning to Perspective restores the previous orbit pose (avoids Top-view Z-up leaking into free orbit)
Transform Handle
Maya-style manipulator — Move: RGB arrows + plane squares + yellow center; Rotate: RGB rings + outer yellow view ring; Scale: axis cubes + plane squares + yellow uniform center. W / E / R or click the mode chip; space chip toggles Wld ↔ Obj. Default length = one grid cell; + / − scale the handle
Orient
Cycle camera up orientation
Capture
Camera icon opens the screen record frame — save and record controls are on the frame toolbar
Theme
Toggle UI chrome dark / light (editor opens in light theme by default). Opening or importing a mesh applies a matching solid viewport plate (light → white, dark → black) unless you already set a custom image, HDR, or gradient
Help
Open this help window (also F1 or Windows → Help…)
Fullscreen
Toggle fullscreen. When Performance is minimized, its compact HUD appears immediately to the right of this icon
Capture & screen record
Save image
Image icon — dropdown with JPEG, PNG, or PNG transparent. Captures the crop region at output resolution
Format
Dropdown — WebM or MP4 (MP4 when your browser supports it; otherwise WebM)
Record
Red film-strip icon — starts recording; icon switches to a stop square while recording
Close
× icon or Esc — closes the frame (Esc stops an active recording first)
Custom size
Choose Custom in Preset — type width × height in px; the crop frame becomes exactly that size on screen (1:1 with output). Drag the corner to scale freely instead
Screen record
Open
Camera icon → Screen record — a crop frame appears over the viewport with the rest of the screen dimmed
Preset
Dropdown on the frame toolbar — 1080p (1920×1080), phone portrait / landscape, tablet portrait / landscape. Pick one to set the starting output size
Resolution
Monospace label on the toolbar shows output pixels (e.g. 1920 × 1080). Updates live while you drag the resize handle
Resize
Drag the bottom-right corner marker to set any frame size. Preset switches to Custom when dimensions no longer match a preset
Format
Format dropdown — WebM (widest support) or MP4 (Safari and some Chromium builds). Unsupported options are disabled
Record
Red film-strip icon starts capture; it switches to a stop square while recording — click again to stop and choose where to save the WebM/MP4. Gizmo, grid, and helpers hide during recording. Timer shows elapsed time
Close
Close or Esc — closes setup; Esc while recording stops and saves
Move frame
Drag the toolbar (empty area or labels) to reposition the crop window. Preset, resolution label, and buttons are not drag handles
View camera
Orbit, pan, and zoom work as usual — drag inside the crop area or on the dimmed viewport outside the frame
Tip
Frame the shot inside the crop area before Record — custom typed resolution captures that region 1:1; corner-drag resize scales output to match the frame size
Display menu
Grey
Meshes: toggle — clay grey material, textures off. Combines with Wireframe and No Light
Color
Meshes: toggle ON by default — lit shaded materials with textures. Turn OFF for flat material color only (maps removed). Mutually exclusive with Grey. Combines with Wireframe and No Light
Wireframe
Meshes: toggle — dark neutral edge overlay. With Color (or Grey) on, shows the textured or grey surface plus dark wires. Wireframe alone (Color and Grey off) shows edges only
No Light
Meshes: toggle — unlit shading; studio lights + env IBL off. Combines with Grey and Wireframe
Gaussian
Splats: default full 3D Gaussian rendering with view-dependent shading
Point Cloud
Splats: each splat as a screen-space dot — a Dot size bar appears under the top menu (0.25×–30×) for editing floaters
Flat Color
Splats: base albedo only (SH degree 0), no view-dependent highlights
Move the camera to frame the selected object(s) — splat, mesh, node, or Group folder. Also F. In ortho, re-aims the current axis view at the selection
Show Joints
Toggle thin color-coded bone lines in the viewport (drawn over meshes)
Show Lights
Toggle light helpers (Outliner lights, glTF lights, studio key). Tint follows each light’s color with a deep contrast remap + solid core marker so helpers stay readable on both dark and light viewport plates (including white lights)
Show Cameras
Toggle render-camera frustum helpers in the viewport (from Animation Edit → Add Render Cam). On by default; helpers hide while looking through a camera (Numpad 0)
Camera Name
In Camera View (Numpad 0), show the active camera name at the bottom of the resolution frame. On by default
Windows menu
Tools Window
Open or close the floating Tools window (Model Info + Scene Settings tabs). Drag the title bar to move, edges/corners to resize. Header ≡ three-step collapse; drag ⠿ on a section header to reorder cards. Opens by default when the editor starts
Timeline
Floating animation window — Windows → Timeline or the top-bar Timeline button. Four tabs: Dope Sheet (key diamonds — drag to retime), Graph Editor (transform curves + lower Volume pane for audio level keys), Material (opacity / emissive / UV animation curves), and Tracks Editor (glTF clip strips + audio strips). See Timeline & animation below
3DGS Simplify Tool
Workbench to reduce splat count — see section below. Opens large under the top bar
Live FPS gauge, frame time, GPU load estimate, renderer stats (draw calls, triangles, …), scene splat/mesh counts, and JS heap when available. Drag title to move; edges/corners to resize (size remembered). Header ↓ minimizes to a compact HUD in the top bar (right of Fullscreen). Click the HUD expand icon or the top-bar Performance icon to restore the full window. HUD on/off preference is remembered
AR Experience
Floating AR panel — Preview 3D on desktop (webcam composite) or Start AR on mobile (World Placement / Image Target tabs). Author Default / On start, gesture Actions (clip + movie + audio), and optional LINK BUTTON HUD links (up to 8). On mobile, Start AR / Export AR zip… pin to the bottom of the scrollable body. Export AR zip from here or File → Export AR zip…. See Web AR help tabs below
Help…
This large centered help window (also F1) — drag title to move, edges/corners to resize; size is remembered
Guided tour…
Step-by-step coach overlay for empty-start samples — Help → Guided tour… (mobile: choose Character animation or Video screen). Highlights targets with a dim mask; you can still click the UI underneath. Back / Next navigate; × ends the tour. Don’t show again on step 1 only. Video flow: VideoScreen_Rise → Tools Window (VideoScreen_geo, Transform Landscape/Portrait, UV Rotate 90°, movie material, Albedo video) → AR Experience → Start AR. Robo flow: robo_touchGesture → Windows → AR Experience → Preview 3D
Guided tour (coach overlay)
When it starts
First visit to ?empty=1 (unless you checked Don’t show again on step 1). Restart anytime from Help → Guided tour…
Mobile shell
Pick Character animation (robo_touchGesture) or Video screen (VideoScreen_Rise) before the numbered steps
Navigation
Next advances (and on action steps can perform the highlighted control). Back returns to the previous step; from step 1 on mobile, Back returns to the flow picker. Last step shows Done
Video screen tips
Step 5 explains Landscape vs Portrait (Rotation Z). Portrait videos can skip the UV step. Step 8 highlights the Albedo texture icon to assign your own video
AR panel
On mobile, Start AR and Export AR zip… stay visible at the bottom of the AR Experience panel while you scroll long Default / On-start lists
Timeline & animation
Open
Windows → Timeline, the top-bar Timeline button, or Edit Mode → Animation Edit. Drag the title bar to move; edges/corners to resize
Playback
Toolbar: Play / Pause (Space), step frames, jump to start/end, Frame / Start / End / FPS. Loop button cycles Loop → Once → Ping-Pong. Bottom scrub bar drags the playhead
Dope Sheet
Keyframe diamonds per track — click to select, drag horizontally to retime, marquee-select multiple keys. Layer column: expand Tran / Rot / Scale to show per-axis rows; eye toggles filter which channels draw. When a timeline audio clip is selected, the lower Volume row shows volume keys (same data as Graph). Insert (I or S) / delete keys. Interp (Auto Smooth / Linear / Constant / Ease) and Tangent (Auto / Free) apply to the selected key; All Keys applies to the whole track
Graph Editor
Upper pane: channel curves for Tran / Rot / Scale — drag keys and Bezier handles. Same layer expand + per-axis visibility as Dope Sheet. Right-click a transform key → Edit Value (or double-click / mobile double-tap) to type frame + TRS numbers. Lower Volume pane: audio button places the selected Model Info clip on the timeline; click a clip in the Volume column list to select it. With a clip selected, insert volume keys (I / S / Insert Key), edit the curve and tangents, and use Edit Value for numeric level (0–2). Prev/next key and interp toolbar follow the volume track while that clip is selected. Deselect timeline audio (empty Tracks background) to key transforms again in the upper pane
Material (timeline)
Timeline → Material tab — key Opacity, Emissive Int, and UV Off U / V / Rotate on the mesh tied to the active material in Model Info (select a material card first). Curves behave like Graph Editor; context menu Edit Value edits the scalar at that key. Material keys export in scene.json and play in the editor, AR Preview 3D, and exported AR zip (alongside Tracks / Default Action clips)
Tracks Editor
Timeline → Tracks Editor tab — Anim and Audio sections. Drop glTF clips from Model Info onto a character’s Anim lanes; drop scene audio onto Audio lanes. Each clip gets its own stacked row (Maya-style layers). Overlapping clips on the timeline crossfade smoothly during playback (scrub or Play)
Active (per character)
Checkbox on each character row in the Tracks layer column. On — Timeline / Tracks drives that mesh in the viewport (overlap blend, timeline Play). Off — Model Info → Animation drives the mesh (full clip list, Play / Pause / Stop, scrub). Timeline and Model Info sliders stay independent. Turning Active off restores panel playback for that character
Export Anim
Timeline toolbar Export Anim — one dialog to export the selected character’s baked animation over the timeline Start–End range as GLB, glTF, or FBX (mesh TRS + morph; material keys stay in timeline sidecar for AR). Pick format in the dialog; target mesh comes from the current Tracks selection / layer (including grouped / non-skinned entries when selected)
Import animation…
Model Info → Animation — append clips from another FBX/GLB/glTF onto the selected character (matched by name/hierarchy). Works on skinned rigs and on grouped / non-skinned mesh entries when the import maps to the same content root
Animation clips
When a mesh has clips (glTF / GLB / FBX), Model Info lists them under Animation — reorder, rename, Play / Pause / Stop, scrub. Use Import animation… to add more takes. Drag a clip row to Tracks Editor to place a strip. With character Active off, all clips remain playable from Model Info without Tracks
Retime clips
In Tracks Editor, drag a strip horizontally to change its start frame. Trim edges or Ctrl/Cmd-drag edges to retime. Use timeline Start / End / FPS for the overall range. There is no separate Record button on Tracks — use timeline Play / scrub for preview
Scene audio
Add audio files in Model Info → Audio (play / loop / clear). Place clips on the timeline via Tracks Editor → Audio (drag-drop or audio button) or Graph Editor → Volume → audio. Select a strip in Tracks or the Graph Volume list, then key volume on the Dope / Graph volume pane. Trim and start frame stay in Tracks Editor. Waveform preview on each strip
Auto Key
When enabled, gizmo / Transform panel edits at the playhead automatically insert keyframes on keyed tracks
What can be keyed
Any Outliner node except the Scene root — meshes, joints, groups, lights, splats, cameras. Custom camera resolution (W×H) is supported on render cameras
Mesh clips (simple)
Model Info ▶ Play / Pause / Stop runs the selected clip (multi-clip FBX/glTF). When character Active is on, Tracks overlap blend drives the viewport instead. Timeline Play runs keyed tracks plus Active Tracks strips together
Saved in exports
Timeline data (transform keys, material keys, audio volume keys, Tracks Editor strips, audio library) is saved in Scene (zip) and included in AR / HTML exports as scene.json timeline + audio sidecar files
Performance monitor
Open
Windows → Performance, or the speedometer icon in the top bar (opens the full resizable window when closed)
Full window
Semicircle FPS gauge (60 FPS = full arc), frame-time bar, estimated GPU load, renderer table, and scene load (splats, gaussians, mesh verts/tris). Updates live while open
Minimize
Header ↓ — saves window size/position and docks a pill HUD after the Fullscreen icon: FPS arc + label, frame ms, expand icon
Restore
HUD expand icon (four corners), top-bar Performance icon, or Windows → Performance — returns to the last full-window size and position
Top bar cycle
Speedometer: closed → full window → HUD → full window
3DGS Simplify Tool
Open
Windows → 3DGS Simplify Tool. Opaque workbench under the top bar; Original | Simplified previews stay side-by-side
Load Original
Drop a splat on Original, click Browse…, or Import from the scene list when splats are loaded. File → Open / Import with the tool open also feeds Original
Formats
.ply · .compressed.ply · .splat · .ksplat · .spz · .sog (same splat family as the editor)
Split
Drag the vertical divider between Original and Simplified to resize the previews
Suggest from Target
Enter a desired final splat count (or use Mobile AR / Half / Keep 75%). Suggest fills Ratio, Merge Cap, KNN, optional opacity/SOR, and SH so Simplify lands near that count. Mobile AR targets ~150K for smoother phone SLAM
Ratio
Fraction of splats to keep after merging (higher = less reduction)
KNN Neighbors
How many nearby splats are considered when building the merge graph
Merge Cap
Maximum share of splats that may be removed by merging (safety limit)
Opacity Prune
Drop splats dimmer than this opacity before merging (0 = off)
Statistical outlier removal for floating isolated splats. Std Ratio 0 disables SOR; Neighbors sets local density sample size
Simplify
Runs opacity → SOR → kNN merge → SH. Uses the local Python API when available (`python run.py`); otherwise runs entirely in the browser (static web hosting / GitLab Pages). Progress shows in Parameters
Result
Shows original vs simplified counts, splat/data reduction, and SH change
SSIM Quality
Calculate SSIM compares Original vs Simplified from the same camera. View-dependent — recalculate after orbiting (note appears when the camera moves)
Replace in scene
Swap the imported scene splat with the simplified model (keeps pose / hierarchy). Enabled only when source was Import from scene
Place as new
Add the simplified splat to the scene (offset slightly when sourced from a scene splat)
Export
Download the simplified model (PLY) to disk
Scene Settings
Color Background
Solid (Color A) or Gradient; Color B, tightness, and position are used only in Gradient
Image Background
Sample photo presets (Warm Evening, Kitchen, Restaurant, Camping, …) plus upload jpg/png/webp or equirectangular HDR/EXR sky, then Cover / Contain / Stretch, Behind color, Scale / Pos / Blur / Rotate. Brightness (0.1–5×) and Exposure (−2…5 EV) adjust the plate look only — they do not change mesh IBL lighting. Background HDR is a sky plate only — it does not light meshes
Background Rotate
Yaw for the photo plate or HDR sky — slider in Image Background (0–360°)
Background Blur
Soft-focus blur on photo plates and HDR/EXR skies (0–40 px). Saved in Scene GLB and HTML export
Lighting — Mesh
Studio lights + HDR environment lighting for glTF / GLB meshes (separate from splat Relight). Apply Lighting writes mesh studio settings onto the default Outliner lights; custom Outliner lights disable this panel.
Apply Lighting
Apply presets, Ambient, Soft Fill, Key, and Exposure to the default Outliner directional + ambient lights. HDR import/rotate and Reflections update live.
Add / Remove Default Lights
Toggle studio ambient, soft fill, and key. When off, HDR / env can still light meshes. Add also restores the studio room env if HDR lighting was removed
Environment Map
Import equirectangular HDR / EXR for image-based lighting (PBR). Lights meshes even when studio lights are off
Env Map Rotate
Yaw for HDR / environment IBL — slider under the Environment Map thumbnail (0–360°). Independent of Background Rotate
Remove HDR Light
Clear the environment map so it no longer lights meshes (studio / Outliner lights can still light them)
Exposure / Reflections
Master brightness and IBL strength for mesh lighting (Reflections = how strongly the HDR lights the mesh). HDR lighting exposure goes up to 10 EV. Image-background plate tone uses separate Brightness / Exposure sliders under Image Background (up to 5× / 5 EV).
Sliders / numbers
Most controls accept type-in values (Enter or blur to apply), including FOV, color grade, and lighting
Post-Processing Filters
Bloom (strength / radius / threshold), FXAA antialiasing, depth of field (auto focus on orbit target or manual focus distance, aperture, max blur), pixelation, and color grading — built-in looks plus Load LUT… (.cube) with intensity. Saved in the scene; applied in the editor viewport, Export as HTML zip, Export AR zip, and AR Preview 3D
Panel collapse
Header ≡ — (1) collapse all sections, (2) shrink to title bar only so the panel stays narrow at the side of the viewport, (3) restore. Same behavior as Model Info
Outliner (Model Info)
Select
Click a row — or click a splat / mesh / light helper in the viewport (meshes in front of a splat win the click). Empty space clears selection
Visibility
Eye icon on each row — hide/show that node. Hidden lights stop lighting the scene; splat Group hide cascades to children
Filter
Multi-select types to show: Meshes, Joints, Lights, Splats, Groups. Matching nodes still appear when a parent type is unchecked (children are promoted)
Rename
Double-click the name, or press F2
Regroup
Middle-drag a Group or node onto Scene or another Group
Resize
Drag the divider under the tree — details (anim, transforms) scroll underneath
Rotate anim
Add rotate anim / Stop rotation on a mesh node, splat Group, or Group folder · Axis X/Y/Z/−X/−Y/−Z · Loop seconds — saved in Scene GLB and HTML export
Animation (mesh)
glTF / GLB / FBX clips list under Model Info → Animation (multiple takes supported). Click a name to select; Play / Pause / Stop and scrub. Import animation… appends clips from another FBX/GLB/glTF onto the selected character. Clips stick through material edits and child deletes — remove only via clip Delete or deleting the asset. Optional: drag a clip to Timeline → Tracks Editor. Bind clip names to AR gesture Actions in the AR Experience panel
Shadows
Selected object (top) and Whole scene (bottom): Cast / Receive. Softness of the penumbra is set per light (Selected Light), not on the material
Selected Light
Model Info section when an Outliner light is selected — Color, Power, Cast shadow, Softness (0–200; 0 = hard edge), Quality (Low–Ultra map resolution for smoother penumbra), Reset. Directional also has Azimuth / Elevation / Distance (aims at world origin). Collapse with the section header; drag ⠿ to reorder alongside Selection, Transform, etc.
Selected Camera
Model Info section when a render camera is selected — FOV (degrees), same range as Scene Settings → Perspective Camera. Updates the frustum helper and Camera View when looking through that camera
Transform (Model Info)
Position / Rotation / Scale
Edit the selected splat, mesh, node, or Group folder — Local Position, Local Rotation, and Local Scale (parent-relative). Sliders + number boxes stay in sync with the gizmo. Undo with Ctrl+Z
Scale lock
Padlock to the right of Scale — locked (blue) keeps X, Y, and Z proportional when editing one axis or dragging a single scale handle; unlock for independent axes
Reset Transform
Set Position / Rotation / Scale back to identity (moves the object back). Also clears a custom pivot bake so identity is a true origin reset
Freeze Transform
Zero Position / Rotation / Scale without moving the object — bakes the current pose into the geometry / hierarchy. Same as Edit → Freeze Transform
Reset Pivot to World
Move only the selected model's pivot to the world / grid center (0,0,0). Clears Position to 0; rotation and scale stay; the object stays put. Same as Edit → Reset Pivot to World
Material (Model Info)
Materials
One section in Model Info: material library cards, then Shader / Sides / properties below when a card is selected. Create new material, Select from viewport, and list view toggles sit above the grid. No mesh selection required to edit a library or draft material — Apply to selected geometry still needs a mesh. Physical shader: Refraction group (Transmission + Thickness + IOR — not Opacity; Dispersion for chromatic split; HDR/env required, enabled when Transmission > 0). Clearcoat & Specular and a collapsed Physical Advanced group (sheen, iridescence, anisotropy). New Physical drafts start with the glass preset.
Shader
Standard (PBR), Phong, Basic, Movie (video + audio), Shadow Catcher, or Occlusion — switching keeps compatible maps when possible
Import — Maya / Blender
On glTF / GLB import, materials normalize to Standard (PBR). Maya aiStandardSurface and Blender Principled BSDF map base color, metalness, roughness, normal, emissive, and opacity. Clearcoat, transmission, and specular extensions are baked into roughness / opacity where Standard cannot represent them. Packed ORM maps: use Channel R/G/B on AO / Rough / Metal (Maya glTF: R=AO, G=Rough, B=Metal)
Import limits
Subsurface, true glass refraction, anisotropy, and separate clearcoat layers are not in Standard (PBR) — those channels are approximated or dropped. Re-export from Maya / Blender with metallic-roughness PBR for best match
Movie
Playback section above Textures (Play/Pause, Stop, Loop, Mute, scrubber). Texture slots accept image or video; sound follows the material’s audio slot (usually Albedo). Saved in Scene zip; HTML zip keeps videos as sidecar files next to the viewer
Audio
Model Info section between UV Edit/Anim and Shadows — choose a scene audio file (play / loop / clear). Included in screen recordings with Movie material audio
Sides
Single-sided (front faces), Single-sided reverse (back faces), or Double-sided
Textures
Each slot: bold name · thumbnail to add/replace · UV1/UV2 · Tile on/off (repeat vs clamp) · × to clear. Data maps have Channel RGB/R/G/B. UV / Tile choices are saved in Scene GLB and HTML export
Standard slots
Albedo (+ Color) · Normal (Intensity 0–5) · Bump (Scale) · Height (Scale / Bias) · Rough · Metal · Emit (Color + Power) · AO · Light · Alpha
Phong slots
Albedo · Normal · Bump · Height · Spec map · Emit · AO · Light · Alpha — plus Specular color / Shininess below
Basic slots
Albedo · AO · Light · Alpha
AO / Light
Intensity sliders; default to UV2 when assigned — use the UV button to switch sets per slot
Height
Vertex displacement — needs dense geometry to look smooth
Alpha
Opacity mask; enables transparency when a map is assigned
Opacity
Overall material opacity (below the texture blocks). Can be animated on the Timeline → Material tab
Shadow Catcher
Transparent surface that only shows shadows from other meshes — including over 3D Gaussian splats in mixed scenes (shadows depth-test against splat geometry, not only flat compositing). Enable Cast shadow on casters; set Softness on the light. Controls: Shadow color and Opacity. Same behavior in the editor, Export as HTML zip, and AR Preview / exported AR zip
Occlusion
Invisible depth mask — hides splats and meshes behind or inside it
Transparency Sorting (Model Info)
Scope
Applies to the whole scene, not just the selected mesh. Use it when overlapping transparent surfaces sort incorrectly.
Disable Depth Writing
Keeps depth testing on, but turns depth writing off for transparent materials so later transparent surfaces are less likely to punch holes into each other. Good first fix for glass, decals, and layered alpha.
Use Alpha Testing
Turns cutout-style transparency into a binary mask. Pixels above the threshold draw fully; pixels below it are discarded. Best for leaves, fences, labels, and hard-edged opacity maps.
Manual Render Order
Builds a scene-wide list of likely transparent meshes and lets you set each object’s renderOrder manually. Higher values draw later, so they usually appear on top of lower values.
Enable Alpha Hashing
Uses hash-based alpha coverage on supported materials instead of standard transparency blending. This can reduce sorting artifacts while keeping soft transparency, but it may introduce fine dithering or noise.
Per object render order
Shown only in Manual Render Order mode. The list includes transparent-looking scene meshes and skips editor helpers such as occlusion and shadow-catcher helpers.
Help button
The ? button next to Mode jumps directly to this help section.
Tip: menus are File · Edit · Edit Mode · Display · Windows. Mesh formats include glTF / GLB / FBX (multi-clip; Tracks Editor with overlap blend, Material timeline, Export Anim). AR Experience: World / Image Target, gesture Actions, material timeline in Preview + AR zip, multi LINK BUTTON HUD — see Web AR tabs. Exports and Save Selection use Save file confirm before disk write; File → Save quick-overwrites Scene zip when allowed. Scene Settings → Post-Processing Filters export with HTML/AR. New in 1.4.0: physics (DYN_ / COL_, per-object settings, skinned colliders, hit triggers) and particles — see the Dynamic & Particle tab; Scene zip hand-off for developers — see the Engineer: Scene zip tab; audio exports as MP3. Splattic Public Beta 1.4.0 · 2026 · Changelog · Legal. For sharing: Export as HTML zip… or Export AR zip… (HTTPS host).
Web AR Guide moved
This bookmark now opens three pages:
Web AR overview — modes, Preview vs export, AR zip contents, hosting
What Web AR means in this editor, how Preview differs from Export, what an AR zip contains, and how to host it. Step-by-step modes live on their own pages.
Powered by 8th Wall (free). World Placement and Image Target work on iOS Safari (including iPad) and Android Chrome — no app install. Camera access needs HTTPS (and permission).
What Web AR is here
Two modes
World Placement (SLAM) — scan a surface, place with the Tomoe marker, then interact. Image Target — anchor content to a printed JPEG/PNG when the camera detects it. Pick the mode with the tabs in Windows → AR Experience.
What can appear
Meshes (GLB / glTF / FBX, including multi-clip animation) and/or 3D Gaussian Splats (.ply / .splat / .ksplat / .spz / .sog). Mix both in one scene. Very large splat counts may be slow on phones — simplify first if needed.
Browser & permissions
Exported experiences must be served over HTTPS. The browser will ask for camera (and on iOS, often motion). file:// will not work for live AR.
Open the panel
Windows → AR Experience. Device capabilities and scene content appear at the top. Author Default / On start, gesture Action rows, and LINK BUTTON in the same panel (shared by both modes).
Outliner visibility
Eye icons in the Outliner hide meshes, lights, splats, and groups in the editor — the same visibility applies in Preview 3D, in-editor mobile AR, and exported AR zip (hidden nodes stay out of the AR scene graph)
Shadow catcher
Shadow Catcher materials (opacity in Model Info) export to AR; opacity is honored in Preview and on device. In mixed mesh + splat scenes, catcher shadows depth-test against splats and composite over splats (same as the editor). Timeline opacity keys on other shaders also play in AR Preview and exported zip
LINK BUTTON
Shared panel block (World and Image Target). Activate master switch; + Add / Delete up to 8 buttons. Each chip has a checkbox to show or hide that button without deleting it. Per button: URL, multi-line title (Enter for a new line), text alignment, font / size / weight / italic / underline, icon (Image / SVG… or clear), icon width, label placement (top / bottom / left / right), gap, optional colored background. Device preview zoom (− / 100% / +) scales the phone frame for precise placement (session-only). Drag buttons in the device preview to position. Checked buttons with a URL appear in Preview 3D and exported AR zip (tap opens the URL in a new tab). Saved as experienceHudButton in scene.json
Preview 3D vs Export AR zip vs mobile
Desktop Preview 3D
Composites the scene over the webcam (or a grid when the camera is denied). Drag to orbit, scroll to zoom. Splats render with Spark in Preview and when you open an exported AR folder on desktop. LMB click ≈ tap; directional swipes use W / A / S / D on desktop only. A dismissible Desktop gestures legend appears in Preview. Mobile Preview / Start AR uses real touch — no WASD keyboard swipes
Desktop Preview Image Target
With the Image Target tab active and a complete target in memory, the button becomes Preview Image Target — webcam + 8th Wall image tracking against your printed marker.
Mobile Start AR
On phone/tablet the primary button is Start AR and launches the real 8th Wall camera session for the active tab (World or Image).
Export AR zip
File → Export AR zip… or Export AR zip… in the AR panel. Packages a self-contained folder ({name}-ar.zip). Mode matches the active AR tab. Opening the zip on Mac/Windows shows desktop 3D preview — live camera AR is for phones/tablets over HTTPS.
First mobile load
The 8th Wall engine still needs internet (CDN) on first session, even when your assets are hosted locally.
AR zip package contents
Exact files depend on mode and scene. The list below matches what the exporter writes today (packArExportZip / README-AR.txt). Do not invent extra paths.
index.html
Primary viewer — start gate, then Start AR on mobile. World Placement or Image Target HTML depending on the active tab at export.
README-AR.txt
Hosting notes and viewer behavior for this package.
THIRD_PARTY_NOTICES.txt
Third-party license notices for bundled libraries.
.nojekyll
Empty marker so GitHub Pages serves the folder correctly.
scene.glb
Mesh geometry when present (plus optional companion mesh files listed in the manifest).
scene.json
Manifest — transforms, materials, lighting, viewport post-processing, timeline (transform + material tracks, Tracks strips, audio), splat list, Scene root scale, plus onStartBinding, gestureBindings[], gestureMedia, experienceHudButton (LINK BUTTON array), and worldPlacement (World Placement options such as twoFingerRotate) when authored.
ar-runtime.js
Bundled scene, lighting, placement, and playback runtime.
libs/
three.module.js, three.core.js, GLTFLoader.js, BufferGeometryUtils.js, RoomEnvironment / HDR / EXR / RGBE loaders when packed, fflate.module.js, and spark.js when the scene includes splats.
Splat / media sidecars
.ply / .splat / .ksplat / .spz / .sog, movie videos, timeline audio, custom .hdr / .exr — only when referenced by scene.json.
Movie video format
By default each Movie material's video is packed as the original file (no re-encode) — e.g. an iPhone recording stays .mov (H.264 or HEVC). For the widest phone-browser support use MP4 (H.264 video + AAC audio). Tick Optimize movies for web (H.264 MP4) in the export dialog to convert non-MP4 movies in the browser while packing (first use downloads the converter; long clips take time). Files that are already MP4 are kept as-is; if conversion fails, the original is packed.
LTomoe.png
World Placement only — Tomoe placement fiducial texture.
targets.json + image-targets/
Image Target only — target metadata and companion JPEGs (_original, _cropped, _thumbnail, _luminance).
Exported HUD
Mobile zip: authored LINK BUTTON chips (when Activate is on), round audio / mute control (label shows mute while sound is on; audio when muted — tap to toggle), and Exit AR. In-editor Preview also has Play / Pause and Stats where applicable
Hosting notes
Unzip fully
Keep every file in the same folder. Paths are relative — do not strip libs/ or media sidecars.
HTTPS
Required for camera. Free hosts: GitHub Pages, Netlify, Vercel, Cloudflare Pages. Local testing needs an HTTPS (or trusted) origin — not file://.
iOS Safari
Allow Camera (and Motion when prompted). Autoplay of unmuted audio often needs a tap — HUD Audio or a gesture unlocks it.
Android Chrome
Allow camera; use HTTPS. Chrome is the supported Android browser for this path.
Quick troubleshooting
Camera denied
Browser site settings → allow camera. iOS: Settings → Safari → Camera → Allow.
Black screen / no load
Confirm HTTPS and that all zip files landed in one folder.
Slow splats
Use 3DGS Simplify Tool (Mobile AR preset ~150K) before export.
SLAM surface placement with the Tomoe marker, pinch scale, and the shared gesture binding tools — from authoring through export.
Workflow — World Placement
1
Build the scene
Import GLB / glTF / FBX and/or splat assets. Arrange transforms in the editor viewport (source of truth for AR layout). Set Scene root scale if the whole experience should be larger or smaller together.
↓
2
AR Experience → World Placement
Open Windows → AR Experience and select the World Placement tab. Author Default / On start and gesture Action rows here (see Gesture binding below).
8th Wall SLAM works on iOS Safari and Android Chrome — no WebXR device required.
↓
3
Preview on desktop
Click Preview 3D to check framing, Default / On-start, and gestures (click + WASD). Fix layout in the editor if anything looks wrong.
↓
4
Place on device
On a phone/tablet, Start AR, allow camera (and motion). Scan a floor or table until the Tomoe marker appears, then tap once to place. After place, Default / On-start and the bed track (if set) become active. Pinch with two fingers to scale the placed scene around the placement pivot. When Two-finger rotate is enabled in the AR panel, twist two fingers to yaw the placed scene.
↓
5
Interact → Export → host
Use authored gestures. Then Export AR zip… (World tab active). Host the unzipped folder on HTTPS. World zip includes LTomoe.png and does not include targets.json / image-targets/.
Scale, rotate & the Tomoe fiducial
Two-finger rotate
World Placement tab — checkbox Two-finger rotate (twist to turn the placed scene). Off by default. When on, a two-finger twist yaws the placed group after Tomoe placement (pinch still scales). Saved in Scene zip and AR export as worldPlacement.twoFingerRotate in scene.json; Preview 3D and exported World zip honor the same setting
Editor vs place
Relative sizes between meshes and splats match the editor. Scene root scale fits everything together. After Tomoe placement, the editor world origin (grid 0,0,0) sits on the fiducial — no automatic bbox grounding. Pinch scales the whole placed group around the placement pivot.
Fiducial circle
The Tomoe is a surface hint — tap places content centered on that marker, facing you (same idea as editor Front). It is not a printed Image Target.
Camera vs scale
Orbit/zoom in Preview only moves the camera — it does not change object scale.
Gesture binding (current tools)
Bindings apply in desktop Preview, in-editor mobile AR, and the exported zip. Leave Action rows empty for autoplay-only (no extra touch listeners). Saved in scene.json as onStartBinding, gestureBindings[], gestureMedia.
Default / On start
Sections: Default action (Start / pause), Background audio (session bed) (Bed track + Volume), Animation (Target mesh + Clip), Movie (Movie + Play mode Loop / Play once), When finished (Consequence + optional Action number). Runs when the experience becomes ready — after place (World) or target detect (Image).
Bed track
Looping session bed from timeline/scene audio. Starts with On-start after place/detect (not while scanning). Volume is separate from per-Action one-shots.
Action rows
Add Action01, Action02, … Each picks a gesture and a panel mode such as Play action (clip + audio + movie), Play random, or play/pause style modes. Clip, audio, and movie blocks sit in collapsible sections per row.
Gestures
Tap, press, swipe up / down / left / right (labels include W/A/S/D), legacy swipe any (WASD), drag, two-finger tap, or No gesture (chain-only via When finished). Each touch type can bind to at most one Action.
When finished
Loop, return to Default, pause / hold, hold first frame, or Play Action number (1-based panel order; chains are capped).
Media transitions
Optional clip blend frames and movie fade frames (0 = hard cut). Duration uses Timeline FPS when set.
Desktop WASD help
Preview shows a Desktop gestures card (Click = tap; W/A/S/D = swipes). Dismissible for the session. Not shown on phone — use touch swipes there. Preview has no camera WASD — gesture keys win while Preview is open
LINK BUTTON
Same multi-button HUD editor as in Web AR overview — up to 8 positioned link chips with typography and icons; export and Preview honor per-button visibility checkboxes
Pinch & twist
Two-finger pinch scales the placed World scene. With Two-finger rotate enabled in the AR panel, twist also yaws the scene. Gesture Actions ignore input while two fingers are down for pinch / twist.
Prepare a printable marker, create the target in the AR panel, preview detection, then export and print or host the target image.
Workflow — Image Target
Same crop/encoding path as @8thwall/image-target-cli. Create Target from Image builds the target in memory first. Preview Image Target works right after create. Persist with Save Scene zip (embeds target JSON + companion images). There is no separate Save / Open / Import Target JSON step in the normal workflow.
1
Prepare the target image
Use a flat JPEG or PNG with high contrast and asymmetric detail. Aim for at least ~640×480 px in the trackable crop. Print at A5 or larger when possible. Avoid large flat colour, repeating patterns, and glossy paper.
↓
2
Assign in the AR panel
Windows → AR Experience → Image Target tab. Select Image (or Take Photo on phone). Optionally crop/straighten, then Create Target from Image. Choose Wall — XY or Floor — XZ, optional Invert X/Y/Z, and Model size vs target. Save Scene zip stores the target; Open Scene zip restores it (thumbnail + tracking) with no Import JSON step.
↓
3
Preview → detect
Desktop: Preview Image Target and point the webcam at the print. Mobile: Start AR and point the back camera. Content appears when the target is detected — no placement tap. Default / On-start and bed audio start after detect. Look away: Movie picture and soundtrack stop. Look back: Movie plays again from the beginning (same clock). The speaker button mutes audio only.
↓
4
Export → print / host
Export AR zip… with the Image Target tab active (blocked until the target is complete with companion JPEGs). Zip includes targets.json and image-targets/ — no LTomoe.png. Host on HTTPS; print the same marker users should scan.
Tips & vs World placement
Lighting & size
Even light, no glare. Larger physical prints track more reliably. Hold at a moderate angle.
Placement options
Wall vs Floor mapping, Invert axes, and Model size vs target are stored in the Scene zip (imageTargets + imageTargetPlacement) and in target JSON metadata (contentPlacement). Editor origin (0,0,0) is the center of the detected target.
Vs World
No Tomoe scan/tap — detection places content. Export omits LTomoe.png and includes target assets. Pinch scale is available on exported Image Target mobile; editor Image Target preview does not include pinch.
Gestures after detect
Same Default / On start and Action system as World (see World placement AR). Bind once; it applies after the marker is found.
Export blocked
Needs a complete in-memory target (after Create, or after Open Scene zip) with companion JPEGs. Export AR zip still writes targets.json + image-targets/.
Image Target troubleshooting
Not detected
Better lighting/contrast; reopen the Scene zip so companion images restore; avoid glossy print. In an AR zip, confirm image-targets/ beside targets.json.
Create disabled / too small
Enlarge the source so the crop is ~640×480 px or larger — Status in the panel explains the error.
Preview black
Allow webcam, Exit, retry. Desktop preview uses the front camera — light the printed marker well.
Generate an invisible collision mesh from a 3D Gaussian Splat (3DGS) directly in the browser. The mesh is saved with the Scene zip and the AR zip, and AR code can raycast against it for ground detection, walking, and teleport.
Using it in the editor
Open Windows → Splat Collision, choose the settings below, then press Generate Collision. Generation uses WebGPU when available (Chrome / Edge, or Safari 26+, over HTTPS or localhost). Without WebGPU a slower CPU fallback runs automatically; the result stats show CPU fallback (no WebGPU).
Splat
Target splat. Defaults to the splat selected in the Hierarchy.
Mode — Object (surfaces)
Collision for surfaces only. Best for single objects and props. Default.
Mode — Ground (floor fill)
Fills solid from the ground up, so the floor has no holes. Best for outdoor scans and terrain.
Mode — Room (fill outside)
Fills everything outside the walls. Best for closed interiors; place the seed inside the room.
Voxel size (m)
Resolution in world metres. This is the setting with the biggest effect on triangle count, file size, and generation time (see Optimization below).
Shape
Smooth — smoothed surfaces, recommended for walking. Blocky (voxel faces) — raw voxel cubes.
Remove floaters
Keeps only the connected part that contains the seed, removing stray floating splats.
Keep only what a walker at the seed can touch
Simulates a capsule (Walker height / Walker radius) starting from the seed and keeps only the reachable surfaces. GPU only — not available in the CPU fallback.
Seed
Pick on splat — click a point on the splat. Auto — use the splat centre. Lift (m) — raise the seed above the picked point (e.g. to stand it above the floor).
Generate / Download / Remove
Generate Collision builds the mesh and shows stats (triangles, GLB size, time, warnings). Download GLB saves {name}.collision.glb. Remove deletes the collision from the splat.
Display
Show collision (wireframe) / Show collision (solid) to inspect the result. Raycast probe — hover the viewport to read Probe: X m from camera · walkable / wall.
Transform afterwards
Moving, rotating, or scaling the splat later does not require regenerating — the collision follows the splat. After a large scale change, regenerate, because the effective voxel size changes with the scale.
What gets saved
Scene zip
{name}.collision.glb, {name}.voxel.json, {name}.voxel.bin (voxel files only from GPU generation). Restored on Open Scene zip — the panel shows Loaded from scene.
AR zip
{name}.collision.glb only, referenced from scene.json as splats[i].collision.
Download GLB
Standalone GLB for use in other tools or engines.
Using it in AR (code)
The collision is hidden by default and excluded from fit / bounds calculations, so it never changes placement or scale. Add ?showCollision to the AR page URL to show it as a wireframe for debugging.
Access
loadSplatsForAR() returns collision. It is also available as window.__arSplatCollision and, in desktop Preview, session.splatCollision.
root / meshes
The collision group (placed directly under the scene) and its meshes.
sync()
Updates the collision transform to follow its splat. Runs automatically before rendering.
raycast(raycaster)
Returns hits sorted by distance. Works while the collision is hidden; collisions of hidden splats are skipped.
setVisible(bool)
Show or hide the wireframe at runtime.
const col = window.__arSplatCollision;
const rc = new THREE.Raycaster();
rc.setFromCamera(ndc, camera); // or rc.set(origin, direction)
const hit = col?.raycast(rc)[0];
if (hit) {
const n = hit.face.normal.clone()
.applyMatrix3(new THREE.Matrix3().getNormalMatrix(hit.object.matrixWorld))
.normalize();
const walkable = n.y > 0.7; // floor; |n.y| < 0.3 is a wall
// hit.point = world-space position (teleport / placement target)
}
Walkable test
n.y > 0.7 = walkable floor; |n.y| < 0.3 = wall. Same rule as the editor Raycast probe.
External GLB use
Vertices are in splat engine space. World transform = splat world matrix × Rz(180°).
Optimization
Voxel size 0.01 m
About 209k triangles — too heavy; avoid in AR.
Voxel size 0.05 m
About 5k triangles (GLB ≈ 90 KB). Props and objects. Default.
Voxel size 0.1 m
About 1k triangles. Rooms and walking.
Voxel size 0.2 m+
Hundreds of triangles. Large outdoor scenes.
For walking and teleport, 5–10 cm is enough. Collision does not need visual detail — keep it coarse.
Match Mode to content
Ground on a single object creates unnecessary walls and fill. Room needs closed walls — if walls are open, the fill leaks (the CPU fallback shows a warning).
Floaters & seed
Keep Remove floaters on and put the seed on the main body of the splat.
Limit raycasts
Raycast only on events (tap, teleport request) instead of every frame. For many rays per frame, consider a BVH such as three-mesh-bvhcomputeBoundsTree (not bundled). For occupancy queries, the voxel files in the Scene zip can be used.
Generation time & memory
Simplify the splat or increase the voxel size. The CPU fallback stops with an error above about 64M grid cells.
CPU fallback limits
No walker carve and no voxel files. For best quality, generate in a WebGPU-enabled Chrome / Edge.
Recommended starting point
Object · 0.05 m · Smooth · Remove floaters on. Check the result with the Raycast probe; if there are too many triangles, increase the voxel size.
Make objects fall, bounce, get thrown and pushed with real-time physics (DYN_ / COL_ naming), and add particle effects such as snow, rain, sparkles and fire. Both work in the editor, desktop AR Preview, the AR zip and HTML exports, and both can be triggered by AR touch gestures.
Quick start — dynamic objects
1. Floor / walls
Add a primitive (Add Primitives → Plane / Box) or import a glTF, and rename it with the prefix COL_ (e.g. COL_Floor). It becomes a static collider.
2. Falling object
Add a Sphere / Box or import a glTF and rename it with the prefix DYN_ (e.g. DYN_Ball). Place it above the floor.
3. Test
Open Scene Settings → Physics and press ▶ Simulate. Press Reset to return everything to its start pose.
4. Interaction
In the AR panel, add a gesture row (e.g. Tap) and set its Physics action to Drop, Throw a copy or Push tapped object.
5. Export
Export the AR zip or HTML — physics runs there too.
Naming convention
Physics is set up by object name only — rename in the Hierarchy (or in your DCC tool before exporting glTF). Prefixes are case-insensitive (col_, Dyn_ also work). Only the topmostCOL_ / DYN_ node in a hierarchy counts; its children are included in that body.
Shortcut: in Edit Mode → Object Edit, select one or more objects and use Physics Prefix → Add DYN_ (Dynamic object), Add COL_ (Collider) or Remove prefix. An existing DYN_ / COL_ prefix is replaced, and each rename can be undone with Ctrl+Z. Exception: Add COL_ on an animated (skinned) character does not rename it — it turns on Skinned collider instead (see Animated characters).
COL_ (collider)
Static, immovable collision. In the editor it is drawn as a green wireframe (toggle with the collider gizmo button). In AR and HTML exports it is invisible — only the collision remains. Use it for floors, tables, walls and invisible blockers.
DYN_ (dynamic)
A rigid body that falls under gravity, bounces and collides. Works for primitives and imported glTF / GLB / FBX models. Its start pose is the pose you set in the editor.
Other objects
No physics — they are neither moved nor collided with.
Collision shapes
COL_ Box / Sphere
Exact box / sphere.
COL_ Cylinder / Cone
Cylinder.
COL_ Plane
Thin box (solid from both sides).
COL_ Torus / glTF
Triangle mesh — follows the real geometry. Keep it low-poly.
DYN_ Sphere / Box / Cylinder
Matching primitive shape. Boxes also land correctly on triangle-mesh colliders.
DYN_ glTF
Bounding box of the model, centred on the model (not on its pivot).
DYN_ bodies also collide with Splat Collision meshes (Windows → Splat Collision), the optional ground plane, and each other.
Editor — Scene Settings → Physics
The section header shows N collider(s) · N dynamic object(s). Simulation in the editor is a preview only: DYN_ objects are automatically reset before Save and before every export, so the saved start poses never change.
▶ Simulate
Releases all DYN_ objects so they fall.
Throw
Throws a copy of a DYN_ object from the current camera, in the view direction.
Reset
Stops the simulation, restores start poses and removes thrown copies.
Maximum number of thrown copies alive at once; the oldest is removed first. Default 20.
Ground plane / Ground Y
An infinite invisible floor at height Ground Y (default 0). Turn it off if your scene has its own COL_ floor or Splat Collision.
Drop on start
Off (default): DYN_ objects stay held in place until a gesture releases them. On: they fall as soon as the AR / HTML scene starts.
Per-object physics — Model Info
Select a DYN_ object and open Model Info → Physics to set how that object moves. Leave a field blank to use the scene value. Settings are saved with the scene and carried into AR zip and HTML export; thrown copies use the settings of their original.
Body shape
Auto (default): primitives use their exact shape; imported models use their bounding box. Pick Sphere for balls and round models so they roll smoothly instead of tipping over corners, or Cylinder for cans and wheels.
Mass (kg)
Blank / 0 = calculated from the object's size × Scene Density. Heavier objects push lighter ones around.
Bounce
0 = no bounce, 1 = bounces back almost fully. When two objects with their own Bounce touch, the higher value is used.
Friction
How much the object grips the floor and other objects (0 = ice).
Air drag
Slows the object's movement over time. Default 0.05.
Spin drag
Slows spinning and rolling over time. Default 0.1; raise it so balls stop rolling sooner.
Reset to scene defaults
Clears every override for this object.
Animated characters — Skinned collider & Follow bone
DYN_ objects can hit an animated (skinned) character and are pushed by it while it moves. Select the character or a COL_ object and use the Physics section of the Model Info panel. Both kinds of collider move with the animation in the editor Simulate, desktop AR Preview, the AR zip and HTML exports.
Do not rename a character to COL_… — a COL_ name makes the whole model one static collider that ignores the animation. Use Skinned collider (below) so the collision follows every bone.
Turn on Skinned collider — method 1: checkbox in Model Info
1. Select the character
Click the model in the viewport or its row in the Outliner (the whole model or any node inside it).
2. Open Model Info
Windows → Tools Window → Model Info tab. Scroll to the Physics section — it only appears for models that contain skinned meshes (bones).
3. Tick Skinned collider
Turn on the Skinned collider checkbox. Green wireframe boxes appear on each bone and follow the animation.
4. Adjust (optional)
Box scale and Min verts / bone appear under the checkbox — see the table below.
5. Test
Add a DYN_ object (e.g. a sphere) and press ▶ Simulate or Throw in Scene Settings → Physics. The counter shows N skinned collider mesh(es).
Turn off
Untick the checkbox. Remove prefix does not turn it off.
Select the character (several objects can be selected at once).
3. Add COL_
Open Physics Prefix in the Object Edit toolbar and choose Add COL_ (Collider).
Result
For a skinned model the name stays unchanged and the Skinned collider checkbox is turned on with default settings (toast: Skinned collider on). If it was already on, nothing changes (Skinned collider is already on). Every other selected object is renamed to COL_… as usual, so a character and a floor can be set up in one step.
Undo
Ctrl+Z turns the Skinned collider off again (and undoes the renames).
Settings
Skinned collider
Shown for models with skinned meshes. Turn it on to build one collision box (or sphere) per bone from the vertices each bone moves. Green wireframes show the boxes in the editor. The model itself does not need a COL_ name.
Box scale
Shrinks or grows every bone box (default 0.9, range 0.1–3). Lower it if objects stop in the air before touching the body.
Min verts / bone
Bones that move fewer vertices than this get no box (default 12). Raise it to skip tiny bones such as fingers.
Follow bone
Shown for COL_ objects. Pick a model and a bone (e.g. RightHand): the COL_ object keeps its current position relative to that bone and then moves with it — use it for a bat, a shield or a hand-made collider that is simpler than the bone boxes. Moving the COL_ object with the gizmo updates the offset.
Undo
All changes in this section can be undone with Ctrl+Z, and they are saved with the scene.
Moving colliders push DYN_ objects but are never pushed back. Very fast motion (over 30 m/s, e.g. a clip jump) is treated as a teleport and does not launch objects. For many characters prefer a few Follow-bone COL_ boxes over Skinned collider on every model.
Particles — adding an emitter
Click Add Particles in the toolbar and choose a preset. An emitter appears in the Hierarchy; select it to edit it in the Particle panel. Move / rotate / scale the emitter with the normal gizmo.
Presets
Snow, Rain, Falling leaves, Sparkles, Dust, Confetti, Fire, Smoke, Magic trail, Burst (one-shot). A preset is only a starting point — every value can be changed.
▶ Play / ⏹ Stop
Start or stop continuous emission (existing particles finish their life).
✸ Burst
Emit the Burst count at once.
Clear / Delete
Remove all live particles / delete the emitter.
Duplicate · Copy / Paste
Select the emitter and use Edit → Duplicate (Ctrl+D) or Copy / Paste (Ctrl+C / V). The copy gets all settings, a new name (…_copy), stays under the same parent node, and is shifted 0.25 m on local X. Undo removes it.
Loop · Play on start
Loop repeats every Cycle seconds. Play on start begins emitting when the AR / HTML scene starts; turn it off for gesture-triggered effects.
Attaching an emitter to a node
To make an effect follow a character or a moving object, drag the emitter in the Outliner onto a mesh node — a bone (e.g. a hand), a DYN_ object or any animated node. The emitter keeps its current world position and then moves, rotates and scales with that node in the editor, desktop AR Preview, the AR zip and HTML exports. Drag it back onto particle_grp (or the Scene row) to detach it.
Mesh row vs inner node
Dropping on the mesh row (the top row of an imported model or primitive) attaches to the whole object — it follows the object's Move / Rotate / Scale and physics. Dropping on a node inside it (e.g. a bone) follows that node's animation.
Space with a parent
Local — the whole effect moves with the node (aura, glow on a hand). World — new particles are emitted from the node but stay where they were born, leaving a trail (sparks from a sword, smoke behind a car).
Unique node names
The link is saved by node name (parentNode) together with the emitter transform local to that node. If several nodes share the name, the first one in scene order is used — the editor warns when you save. Rename nodes so the target is unique.
Missing node
If the node is gone when a scene opens (renamed or deleted), the emitter is kept at the scene root and a notice is shown. Deleting a mesh also detaches its emitters instead of deleting them.
Engineers
particles/emitters.json stores parentNode + local position / rotation / scale. Pass the loaded glTF scene as nodeRoot to createSceneParticles so the emitter is parented to that node in your app (see particles/README.md).
Particle panel settings
Emission
Rate /s (continuous), Burst (count per burst), Cycle s, Lifetime (seconds), Max (particle cap — lower it for mobile).
Shape
Emission volume: point / sphere / box / cone. Size X/R, Size Y, Size Z set its size; Speed and Spread ° set the initial velocity.
Physics
Gravity (negative = rises, e.g. smoke), Wind X / Wind Z, Drag, Turbulence, Sway / Sway Hz (side-to-side drift for leaves and snow), Bounce and Friction on collision.
Look
Size start / end / random, Card H / W × (card aspect), Stretch (velocity streak length), Opacity start / end, Spin / tumble.
Billboard — always faces the camera. Free card — tumbles in 3D (leaves, confetti). Velocity — stretched along the motion (rain, sparks).
Card textures
With texture (card): Add… up to 4 images; each particle picks one at random. The alpha channel is the cut-out and the particle colour tints it (use white to keep the original colours).
World — particles stay where they were emitted, leaving trails when the emitter moves. Local — particles follow the emitter.
Colour
Start → End (fade over life), Random mix (random between the two colours), Rainbow. Set Color start / Color end.
Particle collision
Splat collision
Particles bounce off Splat Collision meshes and COL_ colliders. Must be on for particles to hit COL_ objects.
Ground plane
Particles bounce on the emitter's ground plane.
Die on hit
Particles disappear on their first hit (e.g. rain drops) instead of bouncing.
Particles do not collide with DYN_ objects or with each other. In AR, particles are correctly hidden behind splats, and a pinch-scale of the scene also scales the particle size.
AR touch gestures — Physics & Particles
In the AR panel, each gesture row (Tap, Double tap, Swipe, Two-finger tap…) has a Physics section and a Particles section in addition to its animation / audio actions. One gesture can run all of them together (e.g. Tap = push the object + burst sparkles).
Physics — Drop (release)
Releases held DYN_ bodies so they start falling.
Physics — Throw a copy
Spawns a copy of the target and throws it from the camera (limited by Max thrown). The original object is hidden (no collision either) until Physics — Reset. On a swipe gesture the copy follows the swipe: up = forward along the view, left / right / down = that direction with a slight upward arc.
Physics — Swipe direction
Pushes the targets in the swipe direction, relative to the camera.
Physics — Push tapped object
Pushes the DYN_ object under the finger away from the camera. If nothing is hit, pushes the targets horizontally.
Physics — Reset
Restores start poses and removes thrown copies.
Target
All DYN_ objects or one specific DYN_ object. With All, Throw a copy picks a random DYN_ object each time — including hidden throw-only ones — so pick the specific object when only one ball (one material) should be thrown.
Strength
Impulse strength for throw / swipe / push. Default 3.
Speed from swipe
Throw + swipe gestures only: the copy flies faster for a quick flick and slower for a slow swipe, based on the finger speed at release. Strength becomes the speed of a normal flick (about 3 screen heights per second); the result is clamped between 0.3× and 3×. Desktop W/A/S/D keys have no speed and always use Strength. Off by default.
Hold to throw
Throw a copy on a Swipe up or Swipe any row only (like Pokémon GO, without spin). Touch and hold the screen (about 0.25 s) and the copy appears under your finger, following it and the camera; then swipe up and let go to throw it (aimed through where you let go; Strength and Speed from swipe apply). Letting go without a swipe up — or touching with a second finger — cancels and the copy disappears. A quick swipe without holding does not trigger the row. Desktop AR Preview: hold the left mouse button, drag up and release (W does not trigger it). Off by default; the HTML export throws immediately.
Lifetime (s)
Throw only: each thrown copy is removed from the scene after this many seconds. 0 (default) keeps copies until Max thrown removes the oldest.
Particles — action
Burst particles, Play particles, Stop particles, or Play / Stop particles (toggle).
Particles — Emitter / Burst count
All emitters or one emitter; number of particles per burst (default 60).
Particles — At hit point
Only for Burst particles on a Physics hit row: on (default) = the burst appears where the DYN_ object hit the collider; off = at the emitter's own position. Greyed out for other gestures.
In desktop AR Preview: click = tap, W / A / S / D = swipe up / left / down / right.
Physics hit trigger — play an action when an object hits
Choose the gesture Physics hit (DYN_ collides) in an AR Action row to run that row (animation clip, timeline, audio, particles…) when a DYN_ object hits a collider — for example, the character plays a "hurt" clip when a thrown ball hits it. Several rows can use Physics hit with different settings.
Hit by
Any DYN_ object or one DYN_ object. Thrown copies count as the object they were copied from.
Collider
Any collider, one COL_ object (static or Follow bone), or a model with Skinned collider on.
Min speed
Impact speed (m/s) needed to trigger. Default 0.3. Raise it so resting or rolling contact does not retrigger.
Cooldown (s)
Minimum time between two triggers of the same row. Default 0.5.
Particles
Set the row's Particles section to Burst particles and keep At hit point on to show sparks, dust… exactly where the object hit (the emitter itself does not move).
Physics hit works in desktop AR Preview and the AR zip. It is not run in the editor Simulate or the HTML export. The collider list shows the scene's COL_ objects and models with Skinned collider on.
Activate physics — throw, then react on hit (one row)
For "swipe to throw a ball, the character reacts when it is hit", set a touch gesture row (Tap, Swipe up…) to Action mode → Activate physics (physics + on-hit reaction). The row then shows the Physics section first (default action: Swipe direction for swipes, Push tapped object for other gestures — Throw a copy is often the best choice) followed by Physics hit trigger (On hit). The normal Animation / Audio / Movie sections are hidden: nothing plays on the gesture itself.
Pokémon GO-style throw: use a Swipe up row with Physics Throw a copy and turn on Hold to throw. The player holds the screen to pick up the ball, then flicks up to throw it; the On hit reaction fires as usual when it hits.
Collider
Any collider, one COL_ object or a model with Skinned collider on.
Min speed
Impact speed (m/s) needed. Default 0.3.
Hit window (s)
How long after the gesture a hit still counts. Default 8.
Animation clip
Played on the model that was hit (the Skinned collider model, or the model a Follow-bone COL_ follows). The list shows that model's clips (all models when the collider is Any collider).
Audio file / Offset
Sound played on the hit.
Movie / Play mode
Movie started by the hit (Loop or Play once).
Particles / Emitter / Burst count
Particle action run on the hit: Burst particles, Play particles, Stop particles or Play / Stop particles, on all emitters or one emitter. Burst count = particles per burst (default 60). A hit with only particles set plays no clip, audio or movie.
At hit point
Burst particles only. On (default): the burst appears where the DYN_ object hit (e.g. on the character's head). Off: at the emitter's own position. The emitter does not move either way.
When finished
Applies to the hit reaction clip (e.g. return to the Default action).
Only the DYN_ objects that this gesture threw, pushed or released are watched, and each fires the reaction once. Activate physics is not available for None or Physics hit gestures. The on-hit reaction runs in desktop AR Preview and the AR zip; the HTML export runs only the physics part.
Export & save
Scene zip
Physics settings are saved with the scene; DYN_ / COL_ are kept through their names. Particle emitters and their card textures are saved in the particles/ folder.
AR zip
Physics engine (cannon-es) and particles are bundled. scene.json contains a physics block (settings, colliders, dynamics, targets) and a particleEmitters block.
HTML export
Physics, particles and the physics / particle parts of gesture rows are included (adds about 0.5 MB when physics is used). Controls: click = tap, quick drag = swipe, W / A / S / D = swipe, double-click = two-finger tap.
Tips & limitations
Held until released
Unless Drop on start is on, DYN_ objects float in place until a Drop / Swipe / Push gesture. Combine a Tap → Drop row with Drop on start off for "tap to drop" scenes.
Keep colliders simple
Prefer COL_ primitives (box / sphere / plane) over high-poly glTF meshes — triangle-mesh colliders cost more, especially on phones.
Real-world scale
Physics uses metres. Very small or very large objects behave unnaturally; model at real size and use AR pinch to scale — the physics scales together with the scene.
Fallen objects
Bodies that fall more than 50 m below the ground are reset automatically.
Particle performance
Keep Max to a few hundred per emitter for mobile AR; Additive blending with large sizes is the most expensive.
Quick artist guide: import glTF/GLB/FBX (multi-clip), stage with 3DGS, animate materials and timeline audio volume on the Timeline for AR, wire AR touch gesture Actions, then export AR ZIP, HTML, or Selection. Opens as a separate page (with example videos).
What is inside a Scene zip (File → Save), how to load it in your own three.js app or another engine, and the pitfalls to avoid when you read or edit it.
Which export should I use?
Scene zip
The editor's project file and the engineer hand-off. Reopens in the editor with everything editable, and carries readable data (scene.json) to rebuild the scene in your own app.
AR zip
A finished, self-contained Web AR site (runtime + libraries included). Use it when you want the scene to play as authored without writing code — see Engineer: AR zip.
HTML export
A single-page desktop / mobile viewer of the scene. Same idea as the AR zip, without AR.
Selection export
Plain glTF / GLB / FBX of the selected meshes only — for DCC tools and engines that only need geometry, materials and animation.
Rule of thumb: play as authored → AR zip / HTML export. Rebuild in your own code or engine → Scene zip. Just the model → Selection export.
Zip contents
<name>.glb
Mesh geometry, standard glTF PBR materials / textures, skins and animation clips. Splats, lights, background and special shaders are not in the GLB. A splat-only scene still has a small placeholder GLB.
<name>.js
ES module (three.js r150+) exporting EDITOR_SCENE (the full scene payload) and helpers: applyEditorScene, applyEditorMeshLighting, applyEditorOutlinerLights, applyEditorMeshShadows, applyEditorSceneMaterials, getEditorTimeline, getEditorAudioLibrary. Embedded splats are stored here.
scene.json
The same payload as pretty-printed JSON, for reading without importing the module. Embedded splat bytes are left out — those entries have dataIn naming the .js file.
Sidecar files
Files referenced by name from the payload: environment HDR / EXR (envFile), background image / sky / HDR, texture maps and movie videos used by special materials, timeline audio, splat .ply / .compressed.ply (sidecar mode), splat collision files, and extra mesh GLBs for multi-mesh scenes (meshFiles, catchersFile).
particles/
Only when the scene has particle emitters: particle-core.js (dependency-free runtime), emitters.json, emitters.schema.json and README.md. See Attaching an emitter to a node.
Loading it in three.js
Unzip everything into one folder and serve it over HTTP (file references are relative names). The .js imports three and three/examples/jsm/… with bare specifiers, so use a bundler or an import map.
import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { EDITOR_SCENE, applyEditorScene, getEditorTimeline } from './MyScene.js';
import { createSceneParticles } from './particles/particle-core.js';
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.outputColorSpace = THREE.SRGBColorSpace;
const scene = new THREE.Scene();
// 1. Meshes
const gltf = await new GLTFLoader().loadAsync('./' + EDITOR_SCENE.sceneFile);
scene.add(gltf.scene);
// 2. Lighting, HDR environment, outliner lights, shadows, special materials
await applyEditorScene(scene, renderer, { baseUrl: './' });
// 3. Camera
const { pos, at, fov } = EDITOR_SCENE.cam;
const camera = new THREE.PerspectiveCamera(fov || 50, innerWidth / innerHeight, 0.01, 1000);
camera.position.fromArray(pos);
camera.lookAt(new THREE.Vector3().fromArray(at));
// 4. Particles (optional)
const fx = createSceneParticles(THREE, EDITOR_SCENE.particleEmitters || [], gltf.scene, { nodeRoot: gltf.scene });
fx.start();
// 5. Animation / audio timing (optional) — your own player
const tl = getEditorTimeline(); // { fps, start, end, tracks, mediaStrips, audioLibrary, ... }
applyEditorScene applies lighting, lights, shadows and materials only. Mesh transforms from meshExtras (multi-object scenes), splats, particles, physics, timeline playback and AR gestures are yours to apply — see the sections below.
Timeline (fps, range, keyframe tracks, media strips, audio library) and clip choices per mesh. The timeline is the source of truth for animation placement and audio timing / volume.
particleEmitters
Particle emitters (same data as particles/emitters.json).
physics
Present only when the scene has COL_ / DYN_ objects: settings, colliders, dynamics, targets. Optional: skinned ([{ mesh, scale, minVerts }] — per-bone colliders), follow ([{ name, mesh, bone, offset }] — COL_ node following a bone; offset is a 16-number column-major matrix relative to the bone) and hitColliders (names usable in Physics hit rows).
AR Experience keys
arMode, onStartBinding, gestureBindings, gestureMedia, experienceHudButton, worldPlacement, sceneRoot — what the AR zip runtime uses. Same format as the AR zip; field-by-field reference in Engineer: AR zip → Gesture rows (including physicsHoldThrow and the onHit particle fields).
filter · colorAdjust · postFx · relight
Viewport look: CSS filter, colour adjustment, post effects, splat relighting. Only present when not at defaults.
Transforms & conventions
Axes & units
glTF conventions: right-handed, Y-up, metres. Quaternions are [x, y, z, w].
Mesh wrapper node
A mesh the editor re-encodes is written as a wrapper node named after the mesh (its Outliner name) with the mesh content below it. Look meshes up by that name.
Single re-encoded mesh
The wrapper already carries the mesh transform — load the GLB and it is in place.
Multi-object / original files
In multi-object scenes the wrappers are identity, and with originalMeshPassthrough the GLBs are the original imported files (no wrapper, no editor transform). In both cases the transform lives in meshExtras[i]: rot is Euler XYZ in degrees, and rotation / scale happen about anchor, then pos is added: world(x) = pos + A + R·S·(x − A). Apply it to the mesh named meshExtras[i].name (or the root of its file in meshFiles).
Node names
Particles, physics, materials and AR actions find nodes by name (first match wins). Keep the names you depend on unique.
Splats (3DGS)
Embedded (default)
Splat data is inside the .js module (splats[i].data, compressed PLY). scene.json only lists the entry with dataIn. Simple, but the .js gets large.
Sidecar
Chosen in the save dialog: each splat is a separate .compressed.ply or .ply file named in splats[i].file. Recommended for engineers — load the file directly with your splat renderer.
Transform fields
pos / rot / scl / anchor (same pivot rule as meshes), groupPos / groupRot / groupScl, optional contentOffset, parentIdx, folderId, and attachMi / attachNode when a splat is attached under a mesh.
Renderer
three.js has no built-in splat renderer. The AR / HTML exports use Spark; any 3DGS renderer that reads PLY works.
Particles & physics
Particles
Use particles/particle-core.js with your own THREE (see the example above and particles/README.md). Emitters with parentNode are attached to the node of that name, using the transform local to it.
Physics
Driven by node names: COL_ = static collider, DYN_ = rigid body. physics.settings holds gravity, bounce, friction, throw speed, ground plane and drop-on-start. The exports use cannon-es; any physics engine can rebuild it from these names and settings. Skinned / follow colliders are kinematic bodies updated from the bone world matrices every frame; gesture rows with gesture: "hit" carry hitDynamic, hitCollider, hitMinSpeed and hitCooldownSec.
Gesture physics
Rows with panelActionMode: "activatePhysics" carry physicsAction, physicsTarget, physicsStrength, physicsLifetimeSec, physicsSpeedFromSwipe and physicsHoldThrow (Hold to throw: the copy is held in front of the camera while the finger is down and thrown on an upward release, no spin; only on throw rows with a swipe-up gesture). Their onHit object holds the reaction: collider, minSpeed, windowSec, clipName, audioId, particleAction, particleEmitterId, particleBurstCount and particleAtHit (burst at the contact point instead of the emitter position).
Unity / Unreal / Godot
Meshes
Import the .glb with the engine's glTF importer (e.g. glTFast, Unreal glTF importer, Godot native). For multi-object scenes apply meshExtras transforms yourself.
Axes
glTF is right-handed Y-up metres. Unity is left-handed Y-up; Unreal is left-handed Z-up in centimetres; Godot is right-handed Y-up. Importers convert meshes, but values you copy from scene.json (camera, particle and light positions) must be converted by you.
Lights & materials
Rebuild from sceneLights / meshLight; standard PBR materials come through the GLB, special materials (movie, occlusion, shadow catcher) need engine-side equivalents.
Particles
Map emitters.json to the engine's particle system using emitters.schema.json (units: metres, seconds, degrees for spread, radians/s for spin).
Cautions
Editing for the editor
When a Scene zip is reopened, the editor reads the .js payload first. From scene.json it only takes the AR Experience keys (and falls back to it when the .js is missing or broken). Edits elsewhere in scene.json are ignored while the .js is present.
particles/ is read-only
The editor ignores the particles/ folder on open; it restores emitters from particleEmitters. Change effects in the editor, not in emitters.json.
Flat folder
Keep every file at the zip root with its saved name. References are plain file names; renaming a file without updating the payload breaks it, and the editor matches files by name only (sub-folders are not meaningful).
Do not edit the GLB for the editor
Re-exporting the GLB from a DCC tool can rename nodes or drop the wrapper nodes, which breaks name lookups for materials, particles, physics and AR actions. Change models in the editor or re-import them there.
Timeline vs GLB clips
Clip trims, retimes and placements come from timeline. The GLB holds the source clips (baked only when needed — see bakedTimelineAnim).
Large scenes
Embedded splats make the .js very large (slow to parse, can exceed bundler limits). Save with sidecar splats for engineering use.
Media
Movie materials and audio need their sidecar files; browsers block autoplay with sound until a user gesture.
Licences
Sample assets in the editor have their own licences — see Sample content before shipping them.
What is inside an AR zip (AR panel → Export AR), how to host and test it, which parts you can safely change, and what scene.json controls at runtime.
Which export should I use?
AR zip
A finished, self-contained Web AR site: viewer page, bundled runtime, three.js libraries, models, media and the experience data. Unzip, upload to any HTTPS static host, open the URL. Nothing to build.
Scene zip
The editor's project file. Use it to keep editing, or to rebuild the scene in your own code — see Engineer: Scene zip.
The AR zip is an output, not a project file. Always keep the Scene zip next to it: to change the experience, open the Scene zip in the editor, edit, and export the AR zip again.
Zip contents
index.html
The AR experience itself (no separate landing page): start gate, device detection, 8th Wall loader, Three.js scene and HUD (mute button, desktop key help). World Placement or Image Target, depending on the AR panel tab at export.
ar-runtime.js
Bundled runtime loaded by index.html with import('./ar-runtime.js'): manifest → scene, lighting, placement, timeline / clip playback, gesture Actions, audio / movies, particles, physics (cannon-es) and the desktop preview.
libs/
three.module.js + three.core.js, GLTFLoader.js, BufferGeometryUtils.js, RoomEnvironment.js, HDR / RGBE / EXR loaders, fflate.module.js. Mapped by the import map in index.html (three, three/addons/…, three/examples/jsm/…). libs/spark.js (3DGS renderer) only when the scene has splats.
scene.glb
Mesh geometry, standard PBR materials / textures, skins and animation clips. Multi-mesh scenes add one GLB per mesh (listed in meshFiles).
scene.json
The experience data, pretty-printed: mesh transforms and materials, lighting, timeline, splats, scene root scale, On start, gesture rows, HUD button, particles and physics. Read at every page load — see scene.json keys.
Media sidecars
Files referenced by name from scene.json: audio (timeline.audioLibrary[].file), movie videos, texture maps not stored in the GLB (e.g. <name>_m0_r0_map.png), HDR / EXR environment, splat files (.compressed.ply, .ply, .spz, .sog…), HUD button images.
LTomoe.png
World Placement only: the placement marker (fiducial) texture.
targets.json · image-targets/
Image Target only: target metadata and the tracking images generated in the editor. Both are required.
Deploy notes for this export, third-party licences (three.js, 8th Wall engine, Spark…), and a marker so GitHub Pages serves files as-is.
Hosting & testing
Deploy
Unzip and upload the folder contents so index.html is at the site root (GitLab / GitHub Pages, Netlify, S3 + CloudFront, any static server). Do not upload the .zip itself.
HTTPS
Required for the camera on phones. Over plain HTTP the page shows Open over HTTPS to use the camera.
Internet
The 8th Wall engine (@8thwall/engine-binary, @8thwall/xrextras) is loaded from the jsDelivr CDN when AR starts. Everything else is local to the folder.
Phone / tablet
Real AR. The user must tap Start (camera permission and audio need a user gesture), then scan a surface and tap the marker (World) or point at the printed image (Image).
Mac / Windows
Desktop 3D preview, no camera: drag to orbit, scroll to zoom, right-drag or Shift-drag to pan. Gestures: W / A / S / D = swipe up / left / down / right, click = tap. Hold to throw: hold the left button, then drag up and release.
# Local test (desktop preview) — serve the unzipped folder
cd my-ar-export
npx serve . # or: python -m http.server 8080
# Phone test needs HTTPS — e.g. a tunnel to the local server
npx localtunnel --port 3000 # or ngrok / cloudflared
Opening index.html with file:// does not work: ES modules, the import map and fetch('scene.json') need an HTTP server.
Runtime flow
1. Start
index.html detects the device. Desktop → desktop preview. Mobile → checks HTTPS, loads the 8th Wall engine and asks for the camera.
2. Load
Fetches scene.json (no cache) and scene.glb, then ar-runtime.js applies the manifest: mesh transforms and materials, lighting and environment, splats via Spark, scene root scale.
3. Place
World: the Tomoe marker follows the detected surface; tap places the editor origin on it. Two-finger pinch scales around the scene pivot (two-finger rotate when worldPlacement.twoFingerRotate is on). Image: content is anchored to the tracked image.
4. Play
onStartBinding starts the first clip / movie / background audio, then gesture rows, particles and physics run from touch input (or keys and mouse on desktop).
scene.json keys
v
Format version (currently 1).
meshes
One record per mesh: name, pos / rot / scl / anchor, holderPos / holderRot / holderScl, applyEditorTRS, contentRootName (the node name in the GLB), visibility, shadow flags, materials[] (per-node material overrides, incl. movie / occlusion / shadow catcher), anim / animName (default clip). Primitives add kind: "primitive" and primType.
meshFiles
Multi-mesh layout: [{ file, name }] — extra GLBs loaded next to scene.glb.
Timeline (fps, range, play mode, tracks, strips, audio layers, audioLibrary) and the clip list per mesh. Gesture rows refer to clips by clipName and to audio by audioId.
AR placement root: scl (Scene root scale from the editor — content appears at editor size × scl, in metres), preserveEditorScale, fitMaxDim (auto-fit size when no scale is given, default 1.5 m), animScaleFactor.
arMode
"slam" (World Placement) or "image" (Image Target).
onStartBinding
What plays after placement: defaultCommand, targetId, action (startClip / startMovie / off), clipName, background audio and its volume.
clipBlendFrames (cross-fade between clips) and movieFadeFrames.
experienceHudButton
On-screen link buttons: activated, url, label, image file, position normX / normY (0–1 of the screen) and styling.
worldPlacement
World mode options, e.g. twoFingerRotate.
particleEmitters
[{ id, name, parentNode, position, quaternion, scale, visible, config }] — same emitter format as the Scene zip (config: preset, rate / burst, lifetime, shape, speed, gravity, colours, sprite, collision…).
physics
Present only when the scene has COL_ / DYN_ objects: settings (gravity, ground / groundY, restitution, friction, density, throwSpeed, maxThrown, dropOnStart), colliders / dynamics ([{ name, primType }]), skinned ([{ mesh, scale, minVerts }]), optional follow, hitColliders, targets.
Gesture rows (gestureBindings[])
Common
id, gesture (tap, press, swipeup / swipedown / swipeleft / swiperight, drag, twofingertap, hit, none), targetId (mesh name or *), panelActionMode (playAction, playRandom, playPause, activatePhysics). Each touch gesture can be used by one row only; none rows are reachable only from finishGestureNum.
particleAction (burst, play, stop, toggle), particleEmitterId (an id from particleEmitters), particleBurstCount.
Physics
physicsAction (drop, throw, swipe, push, reset), physicsTarget (a DYN_ name), physicsStrength, physicsLifetimeSec (thrown copies), physicsSpeedFromSwipe, physicsHoldThrow (Hold to throw — only on throw rows with a swipe-up gesture).
onHit
On activatePhysics rows: the reaction when a body moved by this row hits collider faster than minSpeed within windowSec. Carries its own clipName, audioId, particleAction / particleEmitterId / particleBurstCount, and particleAtHit (burst at the contact point instead of the emitter position).
Physics hit rows
gesture: "hit" fires when hitDynamic hits hitCollider above hitMinSpeed, at most every hitCooldownSec.
glTF conventions: right-handed, Y-up, metres. Quaternions are [x, y, z, w]; rot is Euler XYZ in degrees.
Mesh transforms
When applyEditorTRS is true the runtime applies meshes[i] on top of the GLB: rotation / scale about anchor, then pos — world(x) = pos + A + R·S·(x − A). When false the GLB already carries the transform.
Scene root
Everything sits under one placement root. Its scale is sceneRoot.scl; the editor origin lands on the placement marker (World) or the target centre (Image).
Node names
Physics, particles and gesture rows find nodes by name. Physics lists use the GLB node names (contentRootName, which may carry a _1 suffix); gesture targetId uses the mesh name. Keep the names you depend on unique.
Customizing
Tweak values
Safe: edit scene.json and reload — e.g. physicsStrength, physics.settings, particle config, sceneRoot.scl, HUD button url / label. The runtime validates rows on load; invalid values fall back to defaults and invalid rows are dropped.
Swap assets
Replace a media file with one of the same name and format (audio, movie, texture, HDR). For a different model, re-export from the editor — node names in scene.json must match the GLB.
Look & text
The start gate, status text and CSS live in index.html and can be restyled. Keep the element ids, the import map and the Start click handler.
Embedding
Link to the hosted page, or embed it in an <iframe> with allow="camera; gyroscope; accelerometer; magnetometer; autoplay; fullscreen". A full-page link is the most reliable on iOS.
ar-runtime.js
Generated bundle — do not edit. Its exports (applySceneManifest, attachARGestureBindings, startZipDesktopPreview…) are internal to this index.html and can change between editor versions; for your own app, start from the Scene zip.
Cautions
Flat folder
Keep every file in the same folder with its exported name (only libs/ and image-targets/ are sub-folders). Paths are relative; renaming breaks references.
Re-export after editor updates
The runtime is copied into the zip at export time. Editor fixes reach an existing AR site only after you export again and re-upload the whole folder (not just scene.json).
Caching
scene.json is fetched without cache, but CDNs and browsers may cache ar-runtime.js, GLBs and media. After re-uploading, purge the CDN cache or deploy to a new folder.
Do not edit the GLB
Re-exporting scene.glb from a DCC tool can rename nodes or change the hierarchy, which breaks name lookups for materials, physics, particles and gestures.
Mobile limits
Large splats, many particles or 4K movies can exceed phone memory. Movies play most widely as MP4 (H.264 + AAC); audio starts only after the Start tap.
Licences
The 8th Wall engine binary, three.js and Spark are covered in THIRD_PARTY_NOTICES.txt — keep it with the site. Sample assets have their own licences — see Sample content.