Appearance
Feature detection
Not every Scenery feature is available on every platform or every runtime version. Compute shaders are gated behind a future runtime; hand tracking needs a visionOS device; SharePlay is stripped out of App Clip builds.
Authors branch on capability through a single string-keyed registry on environment.features:
javascript
if (environment.features.has('compute')) {
mesh.runCompute('advect', { uniforms });
} else {
stepOnCPU();
}Unknown feature names return false rather than throwing – this matches WebGPU's Set.has semantic and lets forward-looking scripts query features that may land in a future runtime version without crashing on today's runtime.
Listing what's available
javascript
console.log(environment.features.list());
// → ['sharedActivities']list() returns every feature string the current runtime + device actually supports. Use it when debugging ("why isn't my branch firing?") or when reflecting capability state into telemetry.
Current feature strings
| Feature | Today | Notes |
|---|---|---|
compute | Apple-family 7+ devices on iOS 18 / visionOS 2 / macOS 15+ | GPU compute-kernel dispatch. Kernel.fromSource(...) / Kernel.fromAsset(...) compile a kernel; mesh.runCompute(kernel, options) dispatches it against storage: "compute" attribute buffers and / or compute textures. |
sharedActivities | platform-dependent | Multi-user / SharePlay support. false in App Clip and any build without the shared-activities trait. |
cameraFeed | iOS / iPadOS | Live camera-feed texture (via Texture.cameraFeed()). Uses the AR camera capture; not available on Mac Catalyst (no AR camera) or visionOS (different passthrough surface). |
screenGestures | iPhone / iPad / Mac | Screen-space scene.on('pan'|'pinch'|'rotate') and the consume camera claim. Not delivered on visionOS, where input is spatial (eyes + pinch) – use UI panels / buttons there instead. |
instancing | iOS 26 / visionOS 26 / macOS 26+ | GPU mesh instancing via rep.setInstances(Instances.transforms([...])) – render hundreds of copies of a mesh cheaply. Rendering-only (no physics, collision, or per-instance identity); use entity.clone() for objects the user interacts with. |
More feature strings land here as their backends wire up. A name not in the table above always returns false from has() – never throws.
Pattern
Feature detection is the right tool for runtime branching across platforms and capability classes. Use it for:
- Hardware capabilities the device may or may not have (compute, future:
handTracking,planeDetection,sceneReconstruction). - Build-trait features that strip out of compact targets (
sharedActivities, future:gamification). - Runtime-version features (anything reserved for a later API bump).
For pure platform identity – "am I on visionOS?" – read environment.hostingPlatform directly:
javascript
if (environment.hostingPlatform === 'visionOS') {
enableHandTrackingUI();
}When you care about the interaction model rather than the exact OS, environment.deviceCategory is usually the cleaner branch – 'handheld' (iPhone, iPad), 'spatial' (Apple Vision Pro and other headsets), or 'desktop' (Mac, desktop web). It collapses several hosts into the axis that actually drives your decision:
javascript
if (environment.deviceCategory === 'spatial') {
showControlPanel(); // spatial input – use UI, not screen gestures
}Platform identity is single-valued (you're on exactly one host, in one category) and so stays a string property; capabilities form a set and live in the registry. Reach for a capability flag like environment.features.has('screenGestures') when you want the precise "does this specific API work here" answer rather than a form-factor guess.