API Cheatsheet¶
A quick reference for SceneView's most-used APIs. Print it, pin it, keep it next to your keyboard.
Building for Apple platforms?
See the Apple API Cheatsheet for SwiftUI + RealityKit equivalents.
Setup¶
// build.gradle
implementation("io.github.sceneview:sceneview:4.16.10") // 3D
implementation("io.github.sceneview:arsceneview:4.16.10") // AR + 3D
Core Remember Hooks¶
val engine = rememberEngine()
val modelLoader = rememberModelLoader(engine)
val materialLoader = rememberMaterialLoader(engine)
val environmentLoader = rememberEnvironmentLoader(engine)
val model = rememberModelInstance(modelLoader, "models/file.glb") // null while loading
val env = rememberEnvironment(environmentLoader) {
createHDREnvironment("environments/sky.hdr")
?: createEnvironment(environmentLoader)
}
val cameraManipulator = rememberCameraManipulator()
val mainLight = rememberMainLightNode(engine) { intensity = 100_000f }
val cameraNode = rememberCameraNode(engine) { position = Position(0f, 2f, 5f) }
val viewNodeManager = rememberViewNodeManager()
SceneView¶
SceneView(
modifier = Modifier.fillMaxSize(),
engine = engine,
modelLoader = modelLoader,
cameraManipulator = cameraManipulator, // orbit/pan/zoom
cameraNode = cameraNode, // OR fixed camera
environment = env,
mainLightNode = mainLight,
surfaceType = SurfaceType.Surface, // or TextureSurface
isOpaque = true,
viewNodeWindowManager = viewNodeManager, // for ViewNode
onGestureListener = rememberOnGestureListener(
onSingleTapConfirmed = { event, node -> },
onDoubleTap = { event, node -> },
onLongPress = { event, node -> }
),
onTouchEvent = { event, hitResult -> false },
onFrame = { frameTimeNanos -> }
) {
// SceneScope — declare nodes here
}
ARSceneView¶
ARSceneView(
modifier = Modifier.fillMaxSize(),
engine = engine,
modelLoader = modelLoader,
planeRenderer = true,
sessionConfiguration = { session, config ->
config.depthMode = Config.DepthMode.AUTOMATIC
// ENVIRONMENTAL_HDR is the v4.3.0+ library default — pre-set BEFORE this callback.
// Override only to opt back into AMBIENT_INTENSITY for the cost profile.
},
sessionFeatures = setOf(), // e.g., Session.Feature.FRONT_CAMERA
// fillLightNode = null, // v4.3.0+: pass null to disable the dual-light AR baseline
cameraExposure = null, // null = ARCore default; Float (EV) to override
flashMode = Config.FlashMode.OFF, // v4.11+: Config.FlashMode.TORCH for low-light tracking
// playbackDataset = file, // v4.5+: deterministic replay from a recorded MP4 (File)
// playbackDatasetUri = uri, // v4.11+ scoped-storage equivalent (mutually exclusive)
onSessionUpdated = { session, frame -> },
onSessionFailure = { failure -> // v4.11+: typed exhaustive when (#1759)
when (failure) {
is ARSessionFailure.UnavailableArcoreNotInstalled -> { /* install ARCore */ }
is ARSessionFailure.CameraPermissionNotGranted -> { /* request permission */ }
// ... see ARSessionFailure for the full sealed hierarchy
else -> { /* compiler will flag the day a new failure mode is added */ }
}
},
onTouchEvent = { event, hitResult -> true }
) {
// ARSceneScope — declare AR nodes here
}
Recording / playback¶
val recorder = rememberARRecorder()
val status by rememberARPlaybackStatus(arSession) // v4.11+: PlaybackStatus as State
ARSceneView(
playbackDatasetUri = pickedUri, // scoped-storage Uri
onSessionUpdated = { s, _ -> recorder.recordFrame(s) },
onPlaybackFailed = { e -> /* MP4 unreadable */ },
) { /* DSL */ }
Camera exposure override¶
Pass cameraExposure (in EV — exposure value) to correct a washed-out or too-dark camera
preview on devices where ARCore's auto-exposure does not match Camera2's output:
// Fix washed-out camera preview
ARSceneView(
cameraExposure = 1.0f // lower = darker, higher = brighter, null = ARCore default
) { }
// Fix too-dark preview (e.g. on some front-camera setups)
ARSceneView(
cameraExposure = -1.0f
) { }
0f corresponds to the standard middle-grey reference exposure. Start with 1.0f or -1.0f
and adjust by 0.5 EV steps until the preview matches the device's native camera app.
Node Types — 3D¶
| Node | Key Parameters |
|---|---|
ModelNode |
modelInstance, scaleToUnits, centerOrigin, position, rotation, isEditable, autoAnimate, animationName, animationLoop |
CubeNode |
size: Size, materialInstance |
SphereNode |
radius: Float, materialInstance |
CylinderNode |
radius, height, materialInstance |
PlaneNode |
size: Size, materialInstance |
LightNode |
type: LightManager.Type, apply = { intensity(); color(); castShadows() } |
ImageNode |
imageFileLocation / imageResId / bitmap, size |
VideoNode |
videoPath (simple) / player: MediaPlayer (advanced), chromaKeyColor, size |
ViewNode |
windowManager, content = @Composable |
TextNode |
text, fontSize, textColor, backgroundColor, widthMeters |
BillboardNode |
bitmap, widthMeters, heightMeters |
LineNode |
start, end, materialInstance |
PathNode |
points: List<Position>, closed, materialInstance |
DynamicSkyNode |
timeOfDay (0-24), turbidity, sunIntensity |
FogNode |
view, density, height, color, enabled |
ReflectionProbeNode |
filamentScene, environment, position, radius, cameraPosition |
PhysicsNode |
node, mass, restitution, linearVelocity, floorY, radius |
MeshNode |
primitiveType, vertexBuffer, indexBuffer, materialInstance |
Node |
position, rotation, scale + child content |
SecondaryCamera |
apply — non-active camera (formerly CameraNode) |
Node Types — AR¶
| Node | Key Parameters |
|---|---|
AnchorNode |
anchor: Anchor + child content |
HitResultNode |
xPx, yPx + child content (reticle) |
AugmentedImageNode |
augmentedImage + child content |
AugmentedFaceNode |
augmentedFace, meshMaterialInstance |
CloudAnchorNode |
anchor, cloudAnchorId, onHosted + child content |
Common Node Properties¶
node.position = Position(x, y, z) // meters
node.rotation = Rotation(x, y, z) // degrees
node.scale = Scale(x, y, z) // multiplier
node.isVisible = true
node.isEditable = true // pinch-scale, drag-move, rotate
node.isTouchable = true
node.onSingleTapConfirmed = { event -> true }
node.onFrame = { frameTimeNanos -> }
// Smooth movement
node.transform(position = Position(2f, 0f, 0f), smooth = true, smoothSpeed = 5f)
node.lookAt(targetNode)
// Animation
node.animateRotations(Rotation(0f), Rotation(y = 360f)).also {
it.duration = 2000
it.repeatCount = ValueAnimator.INFINITE
}.start()
Math Types¶
import io.github.sceneview.math.*
Position(x = 0f, y = 1f, z = -2f) // Float3, meters
Rotation(x = 0f, y = 90f, z = 0f) // Float3, degrees
Scale(1.5f) // uniform
Scale(x = 2f, y = 1f, z = 2f) // non-uniform
Direction(x = 0f, y = 1f, z = 0f) // unit vector
Size(width = 1f, height = 0.5f) // Float2
Resource Loading¶
// Composable (preferred)
val model = rememberModelInstance(modelLoader, "models/file.glb")
// Imperative
val model = modelLoader.loadModelInstance("models/file.glb")
modelLoader.loadModelInstanceAsync("models/file.glb") { instance -> }
// Environment
environmentLoader.createHDREnvironment("environments/sky.hdr")
environmentLoader.createKtxEnvironment("environments/studio.ktx")
// Material
materialLoader.createColorInstance(Color.Red)
Custom 3D content¶
Authoring your own model? See the Blender pipeline recipe:
export .glb from Blender for Android (native), or convert .glb → .usdz via Reality
Converter + Reality Composer Pro for Apple platforms.
Threading Rules¶
| Safe | Unsafe |
|---|---|
rememberModelInstance(...) |
modelLoader.createModelInstance(...) on IO |
loadModelInstanceAsync(...) |
materialLoader.createMaterial(...) on IO |
Any composable in SceneView { } |
Direct Filament API on background thread |
Rule: Filament JNI = main thread only. remember* hooks handle this for you.
AR debug — Rerun.io¶
Stream ARCore frames into the Rerun viewer for scrub-and-replay debugging.
import io.github.sceneview.ar.rerun.rememberRerunBridge
@Composable
fun ARDebugScreen() {
val bridge = rememberRerunBridge(rateHz = 10, enabled = BuildConfig.DEBUG)
ARSceneView(onSessionUpdated = { s, f -> bridge.logFrame(s, f) })
}
| Mode | Sidecar command | Shareable? |
|---|---|---|
| Live | python rerun-bridge.py |
No — viewer is local-only |
| Save | python rerun-bridge.py --save |
Yes — writes a .rrd file |
Save & Share trigger from the app:
bridge.requestSaveAndShare { result ->
// result.path -> /Users/dev/.sceneview/recordings/<ts>.rrd
// result.viewerUrl -> https://sceneview.github.io/rerun/?url=<…>
// result.events -> 1234
}
Drop the saved .rrd onto https://sceneview.github.io/rerun/ to scrub
the AR session frame-by-frame in any browser — no install required, no
re-hosting needed for local inspection. To share with a remote teammate,
re-host the file (R2, GitHub release, gist) and send them
https://sceneview.github.io/rerun/?url=<encoded-public-url>.
Spatial Audio & Haptic — cross-platform availability¶
v4.12.0 shipped Spatial Audio (#1900) and Haptic Feedback (#1901). Both have real implementations on all three platforms — they are not Android-only — each using the platform-native audio / vibration backend:
| Feature | Android | Web (sceneview-web) |
iOS |
|---|---|---|---|
| Spatial Audio | SpatialAudioNode { } composable |
io.github.sceneview.web.audio.SpatialAudioNode — a real Kotlin/JS class backed by the Web Audio PannerNode (HRTF panning + distance falloff). Load assets with loadAudioSource(url) / loadAudioSourcePromise(url); drive the listener with setSpatialAudioListenerPose(...). Exposed to Kotlin/JS consumers — it is not @JsExport-ed to a plain-JavaScript sceneview.js global (the module uses suspend, external Web Audio declarations and a sealed interface, none @JsExport-compatible). The web Spatial Audio demo (#1944) uses this API. |
SpatialAudioNode.spatial(...) |
| Haptic Feedback | rememberHapticFeedback() → SceneViewHaptic |
io.github.sceneview.web.haptic.SceneViewHaptic — semantic presets via the Web Vibration API (navigator.vibrate). @JsExport-ed — callable from plain JavaScript as sceneview.haptic.light() / .success() / .continuous(intensity, durationMs) etc. Durations only — intensity / sharpness are accepted for cross-platform parity but ignored at runtime (the Vibration API has no amplitude control). Silent no-op on browsers without navigator.vibrate (most desktop, Safari iOS). |
SceneViewHaptic() (Core Haptics) |
See the Apple API Cheatsheet for the iOS maturity detail.
Apple platforms¶
Building for iOS, macOS, or visionOS? See the Apple API Cheatsheet.