https://framer.com/m/ImageDepthScene-p17O.js

Io / Enhancements / SpatialScene

SpatialScene

SpatialScene turns a photograph into a relief. You give it the image and a depth map, and it builds a mesh that leans with the pointer — or with the device, on hardware that has no pointer at all.

Enhancements25 controlsdepth mappointer and device tilt
Enhancements5 controlspointer parallaxtwo layers

Examples

Live specimens for every control the component exposes.

Default

image is the photograph and depth is its depth map: greyscale with white nearest, or a Turbo rainbow, which is decoded for you. Everything else is tuning.

Relief depth

meshDepth is the Z extent of the relief — how far the scene reads back from the surface. Small values behave like a lens; large ones exaggerate into a diorama. focus picks the depth that stays still while the rest moves around it.

meshDepth 0.5

meshDepth 1

meshDepth 2

Props

The controls exposed on the instance panel.

PropTypeBehaviour
imageimageThe photograph the relief is built from.
depthimageIts depth map: greyscale with white nearest, or a Turbo rainbow, which is decoded automatically.
altstringAlt text for the image.
fitenumcontain · cover. Contain is the upstream behaviour; cover is this build's addition.
focusnumberThe depth that stays still while everything else moves around it.
sensitivitynumberHow far the relief swings for a given pointer or tilt movement.
meshDepthnumberZ extent of the relief — how deep the scene reads. Rebuilds the mesh.
easingnumberPointer only. Higher is snappier.
expandDepthmapRadiusnumberDilation passes over the depth map. Raise it if edges smear into stretchy lines.
meshResolutionnumberMesh grid size. 512 keeps the build under about a second; upstream uses 1024.
depthSizeCapnumberThe depth map is downsampled to this before processing.
trackingenumelement · window — whether the pointer is measured against the component or the whole page.
idleMovementbooleanCircular drift when nobody is interacting. Always off while the gyro is live.
idleAfternumberSeconds of stillness before that drift starts.
gyrobooleanDevice tilt on touch hardware. This is what makes the effect work without a pointer.
gyroRangenumberDegrees of tilt mapped to the full swing.
tiltEasingnumberSeparate from pointer easing; tilt wants roughly 0.15 to 0.25.
gyroInvertXbooleanFlips the horizontal tilt axis.
gyroInvertYbooleanFlips the vertical tilt axis. On by default.
gyroLabelstringiOS only: the chip inviting the reader to tilt. Clear it and the chip never appears.
promptDelaynumberSeconds before that chip is offered.
promptThemeenumdark · light — the chip's colour scheme.
promptAlignenumleft · center · right — where the chip sits.
deniedLabelstringShown instead when the reader refuses motion access.
maxDPRnumberCaps the device pixel ratio the scene renders at.
PropTypeBehaviour
imageimageThe photograph the relief is built from.
depthimageIts depth map: greyscale with white nearest, or a Turbo rainbow, which is decoded automatically.
altstringAlt text for the image.
fitenumcontain · cover. Contain is the upstream behaviour; cover is this build's addition.
focusnumberThe depth that stays still while everything else moves around it.
sensitivitynumberHow far the relief swings for a given pointer or tilt movement.
meshDepthnumberZ extent of the relief — how deep the scene reads. Rebuilds the mesh.
easingnumberPointer only. Higher is snappier.
expandDepthmapRadiusnumberDilation passes over the depth map. Raise it if edges smear into stretchy lines.
meshResolutionnumberMesh grid size. 512 keeps the build under about a second; upstream uses 1024.
depthSizeCapnumberThe depth map is downsampled to this before processing.
trackingenumelement · window — whether the pointer is measured against the component or the whole page.
idleMovementbooleanCircular drift when nobody is interacting. Always off while the gyro is live.
idleAfternumberSeconds of stillness before that drift starts.
gyrobooleanDevice tilt on touch hardware. This is what makes the effect work without a pointer.
gyroRangenumberDegrees of tilt mapped to the full swing.
tiltEasingnumberSeparate from pointer easing; tilt wants roughly 0.15 to 0.25.
gyroInvertXbooleanFlips the horizontal tilt axis.
gyroInvertYbooleanFlips the vertical tilt axis. On by default.
gyroLabelstringiOS only: the chip inviting the reader to tilt. Clear it and the chip never appears.
promptDelaynumberSeconds before that chip is offered.
promptThemeenumdark · light — the chip's colour scheme.
promptAlignenumleft · center · right — where the chip sits.
deniedLabelstringShown instead when the reader refuses motion access.
maxDPRnumberCaps the device pixel ratio the scene renders at.

Usage

Guidance carried over from the component definition.

Reach for SpatialScene

A photograph should gain depth without being rebuilt in 3D.

You can supply a depth map alongside the photograph.

The motion should answer the pointer or the device, not the scroll.

The scene should also respond on phones, where device tilt replaces the pointer.

Reach for something else

You have the photograph but no depth map, and cannot generate one.

The picture should drift with the page — use ImageParallax.

The page must not ask iOS readers for motion access.

Io — Enhancements

Every specimen above is a live component instance. Colour, type and radius resolve from the project theme tokens.

io

An experiment in what a design system becomes when AI can consume it.

© 2026 io

Contact me

Create a free website with Framer, the website builder loved by startups, designers and agencies.