| name | sceneview-web |
| description | Build 3D and WebXR (AR/VR) experiences in the browser with SceneView for Web — Filament.js (WebGL2/WASM) wrapped in a Kotlin/JS DSL and a plain-JavaScript API on `window.sceneview`. Use whenever the user asks for "3D in the browser", "a web model viewer", "WebXR AR/VR", or any browser 3D/AR app where the dependency is the `sceneview-web` npm package or its CDN script. For Jetpack Compose use the `sceneview` skill; for SwiftUI use `sceneview-ios`. Skip for raw Three.js / Babylon.js / model-viewer / A-Frame work. |
| license | Apache-2.0 |
| metadata | {"author":"SceneView","source":"https://github.com/sceneview/sceneview","last-updated":"2026-05-21","keywords":["sceneview","sceneview-web","3d","web","webxr","webgl","wasm","filament","filament.js","model viewer","augmented reality","virtual reality","kotlin/js","gltf","glb"]} |
What SceneView for Web is
SceneView for Web is the browser half of the SceneView SDK. It renders with
Filament.js — the same Filament engine as SceneView Android, compiled to
WebAssembly + WebGL2. It ships two API surfaces:
-
Kotlin/JS DSL — SceneView.create(canvas, configure = { … }) with a
type-safe builder, plus ARSceneView / VRSceneView / WebXRSession for
WebXR. This is the source-of-truth API; the package is built from
sceneview-web/src/jsMain/.
-
Plain-JavaScript API — when loaded via a <script> tag the library
registers itself on window.sceneview, exposing createViewer,
modelViewer, etc. for use with no bundler and no Kotlin.
-
npm package — sceneview-web (currently 4.16.10).
-
Renderer — Filament.js (WebGL2/WASM). Requires Chrome 79+, Edge 79+,
Firefox 78+, Safari 15+.
Authoritative API reference
Always treat llms.txt in the repo root as the source of truth — its
"SceneView Web (Kotlin/JS + Filament.js)" section carries the complete DSL,
the JS API, the WebXR ARSceneView / VRSceneView / WebXRSession surface,
and the threading rules.
https://github.com/sceneview/sceneview/blob/main/llms.txt
The Kotlin/JS source lives in sceneview-web/src/jsMain/kotlin/io/github/sceneview/web/
— SceneView.kt (the DSL + SceneViewBuilder), SceneViewJS.kt (the
JS-facing SceneViewer class), Main.kt (the window.sceneview bindings),
and xr/ (WebXR). The samples/web-demo/ app is a working reference.
When to use this skill
Trigger on any of:
- "Render a glTF / GLB model in a browser."
- "Build a web 3D viewer / product configurator."
- "Add WebXR AR or VR to a web page."
- "Embed a 3D scene with no build step / just a
<script> tag."
- "Use SceneView from Kotlin/JS."
Skip for raw Three.js, Babylon.js, <model-viewer>, A-Frame, or PlayCanvas
work that does NOT use sceneview-web.
Setup — two ways
Script tag (no bundler)
filament.js MUST load before sceneview-web.js:
<canvas id="viewer" style="width:100%;height:100vh;display:block"></canvas>
<script src="https://sceneview.github.io/js/filament/filament.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sceneview-web@4.16.10/sceneview-web.js"></script>
<script>
sceneview.modelViewer('viewer', 'https://sceneview.github.io/models/platforms/DamagedHelmet.glb')
.then(function (sv) { sv.setAutoRotate(true); });
</script>
npm
npm install sceneview-web filament
The minimal correct example — plain JS
The JS API is registered on window.sceneview after the script loads.
Verified against Main.kt (jsModelViewer, jsCreateViewer):
sceneview.modelViewer('viewer', 'model.glb')
.then(function (sv) {
sv.setAutoRotate(true);
sv.setBackgroundColor(0.05, 0.05, 0.12, 1.0);
sv.setEnvironment('studio_ibl.ktx');
});
sceneview.createViewer('viewer').then(function (sv) {
sv.loadModel('model.glb').then(function () { sv.fitToModels(); });
});
SceneViewer instance methods (from SceneViewJS.kt): loadModel(url) →
Promise, setEnvironment, setEnvironmentWithSkybox, setCameraOrbit,
setCameraTarget, setAutoRotate, setAutoRotateSpeed, setZoomLimits,
setBackgroundColor, fitToModels, startRendering, stopRendering,
resize, dispose.
The minimal correct example — Kotlin/JS DSL
Verified against SceneView.kt (create, SceneViewBuilder):
SceneView.create(
canvas = canvas,
configure = {
camera {
eye(0.0, 1.5, 5.0)
target(0.0, 0.0, 0.0)
fov(45.0)
}
light {
directional()
intensity(100_000.0)
direction(0.6f, -1.0f, -0.8f)
}
model("models/helmet.glb") { autoAnimate(true) }
cameraControls(true)
autoRotate(true)
},
onReady = { sceneView -> sceneView.startRendering() }
)
The minimal correct WebXR AR example
Verified against xr/ARSceneView.kt. AR session creation MUST happen inside a
user-gesture handler (a click/tap listener):
ARSceneView.checkSupport { supported ->
if (supported) {
ARSceneView.create(
canvas = canvas,
features = WebXRSession.Features(
required = arrayOf(XRFeature.HIT_TEST),
optional = arrayOf(XRFeature.DOM_OVERLAY, XRFeature.LIGHT_ESTIMATION)
),
onError = { msg -> console.error(msg) },
onReady = { arView ->
arView.onHitTest = { pose -> arView.loadModel("models/chair.glb") }
arView.onSelect = { source -> }
arView.start()
}
)
}
}
WebXR VR uses the same shape via VRSceneView; WebXRSession is the
lower-level unified AR+VR API. See llms.txt § WebXR.
Critical rules (verified — do not break)
-
Load filament.js BEFORE sceneview-web.js. The library needs the WASM
Filament module present at init. Use <script> tags, not ES imports, for
the no-bundler path.
-
The canvas must have non-zero pixel dimensions. createViewerImpl
falls back to clientWidth/clientHeight if width/height are 0, so the
canvas must be laid out (e.g. 100vw/100vh or fixed px) before
createViewer runs.
-
Everything is async. SceneView.create and loadModel are
Promise-based — .then(...)/await them before calling instance methods.
loadModel's onLoaded fires only once external textures are fetched.
-
WebXR session creation must be in a user gesture. Browsers reject
requestSession outside a click/tap handler. Always checkSupport first,
then call ARSceneView.create / VRSceneView.create from the handler.
-
One JS main thread. There are no background threads in browser JS — all
Filament calls run on the main thread. Never call destroy()/dispose()
inside an animation-frame callback; defer to the next microtask.
-
WebXR support is partial. AR: Chrome Android 79+, Meta Quest Browser,
Safari iOS 18+. VR: Meta Quest Browser, desktop Chrome with a headset.
Always gate on checkSupport and provide a non-XR fallback.
Haptic feedback
sceneview-web exposes a SceneViewHaptic class that wraps the browser
Vibration API
(navigator.vibrate(...)) behind the same seven semantic presets as the
Android and iOS libraries, so cross-platform code paths stay symmetric.
sceneview.haptic.light();
sceneview.haptic.success();
sceneview.haptic.continuous(1.0, 200);
sceneview.haptic.pattern([10, 50, 20]);
- Presets:
light() medium() heavy() success() warning()
error() selection(). Plus continuous(intensity, durationMs) and
pattern(durationsMs[]).
- The Web Vibration API exposes durations only — there is no intensity
knob — so the
intensity argument is accepted for cross-platform parity
but ignored at runtime.
- Desktop browsers and Safari on iOS do not expose
navigator.vibrate;
every call is then a silent no-op. Some browsers also restrict
vibration to user-gesture handlers. See llms.txt § Haptic Feedback.
Performance / hot paths
Preallocate scratch arrays and mutate them in place — never build fresh
[x, y, z] / float3(...) / mat4 array literals inside a requestAnimationFrame
tick. Filament.js reads the array synchronously, so reuse is safe, and the small
JS heap on iOS Safari turns per-frame allocation into a GC sawtooth that drops
frames. SceneView's own OrbitCameraController keeps eyeScratch / centerScratch
/ upScratch and rewrites them per frame instead of allocating — follow that
pattern in your render loop. Full cross-platform guidance:
docs/docs/performance.md § Hot Paths & Allocation-Free APIs
(audit umbrella #2263).
Resources
- Cheat sheet — the Kotlin/JS DSL, the JS API,
and the WebXR surface, with signatures pulled from
sceneview-web/src/.
- Recipes — model viewer, custom scene,
procedural geometry, WebXR AR/VR — each with the verified entry point.
- Migration — Three.js /
<model-viewer> →
sceneview-web, and cross-platform parity notes.
Workflow guidance
When the user asks for a SceneView-Web feature:
- Pick the API surface. Plain JS (
window.sceneview, script tag, no
build) vs Kotlin/JS DSL (SceneView.create, bundler). Match the user's
stack — don't give Kotlin to a vanilla-JS project.
- Load filament.js before sceneview-web.js in any HTML you generate.
- Give the canvas explicit dimensions.
- Treat creation and
loadModel as async — chain with .then/await.
- For WebXR,
checkSupport first, create inside a click handler, and
provide a non-XR fallback path.
- Read
llms.txt § SceneView Web for the full surface before inventing an
API. The DSL, JS API, and WebXR sections are exhaustive.