Skip to content

add magic_window: segment people out of the camera onto a swappable backdrop#387

Open
salmanmkc wants to merge 15 commits into
google:mainfrom
salmanmkc:demos/magic-window
Open

add magic_window: segment people out of the camera onto a swappable backdrop#387
salmanmkc wants to merge 15 commits into
google:mainfrom
salmanmkc:demos/magic-window

Conversation

@salmanmkc

@salmanmkc salmanmkc commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

New demo. uses MediaPipe ImageSegmenter (the multiclass selfie model, which wasn't wired up anywhere in the SDK yet) to cut people out of the device-camera feed in real time and drop them onto a swappable backdrop, shown on a floating window in the scene.

per frame it grabs a camera snapshot, runs the segmenter for a category mask, and composites in a shader: person pixels kept, background replaced with passthrough (see straight through to the room), a solid colour, or a gradient. the camera comes straight from the SDK camera module (enableCamera('user')), so the desktop simulator pulls the real front webcam and a headset uses its world-facing camera, no demo-side getSnapshot patching. the window sizes itself to the camera aspect so the feed isn't stretched.

A draggable uiblocks control panel swaps backdrops, same ManipulationBehavior card + renderOrder/depthTest treatment as the other demos so the labels aren't occluded by their backplate. keyboard B cycles them too.

prototyped demo-first; the segmenter wrapper (SegmenterController) is kept self-contained so it can be lifted into a world/segmentation addon later.

Passthrough should work on XR devices (the window frame is a ring now so the cut-out shows the real world through the middle, not a dark backplate).

updated passthrough:
updated passthrough

ignore the passthrough being black in the video below (older video)

imagesegment2.mp4

New demo that uses MediaPipe ImageSegmenter (multiclass selfie model) to cut
people out of the device-camera feed in real time and composite them onto a
swappable backdrop, shown on a floating window in the scene.

Per frame it grabs a camera snapshot, runs the segmenter to get a category
mask, and composites in a shader (person pixels kept, background replaced with
passthrough, a solid colour, or a gradient). Press B to cycle backdrops. A
webcam fallback gives the simulator a real person to segment; on a headset the
world-facing camera feeds it instead.
Replaces the keyboard-only backdrop cycling with a spatial control panel you
can grab and place. Uses the uiblocks ManipulationBehavior card pattern with a
tap-to-cycle backdrop button that updates its label in place, and pins the
nested label/icon render order + depthTest so the text isn't occluded by its
own backplate. Keyboard B still works as a fallback.

Adds the uiblocks importmap entries (the @pmndrs/* uikit stack, signals,
yoga) and the missing three/ subpath mapping uikit needs.
@salmanmkc salmanmkc marked this pull request as ready for review June 17, 2026 13:46
@ruofeidu

Copy link
Copy Markdown
Collaborator

Love it! Thank you Salman!!

@salmanmkc

Copy link
Copy Markdown
Contributor Author

Love it! Thank you Salman!!

my pleasure! thanks for the quick review!

Comment thread demos/magic_window/WebcamFallback.js Outdated
// Webcam fallback for desktop / simulator mode.
//
// The SDK reads frames from `xb.core.deviceCamera.getSnapshot()`, which on a
// real Quest returns frames from the passthrough cameras. On desktop the

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: avoid using Quest in the comments, but XR device

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure will change it!

Comment thread demos/magic_window/WebcamFallback.js Outdated

function shouldUseWebcamFallback() {
// Only activate when the simulator addon is in charge (i.e. we're not in
// an immersive WebXR session). On a real Quest this returns false and the

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: avoid using Quest in the comments, but XR device

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will change!

Comment thread demos/magic_window/WebcamFallback.js Outdated
// If the user denies, the SDK keeps using the original `getSnapshot`
// (which returns blank/scene frames) and the demo degrades to "no face
// detected" without crashing.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can have this for now.
Yet I remember we can simply set simulator webcam in the camera module.

the next camera is kind of broken --- I'll talk with Nels in the afternoon.
https://xrblocks.github.io/docs/templates/Camera/

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oo great thanks, looks like Nels did the fix #392 I will adjust it now

salmanmkc added 10 commits June 18, 2026 08:30
…ng getSnapshot

enableCamera('user') makes XRDeviceCamera go straight to the real webcam in
the simulator (facingMode 'user' bypasses the simulator camera), so the
getSnapshot monkeypatch in WebcamFallback is no longer needed. camera cycling
to the real device before permission was the blocker, fixed in google#392.
…stretched

the old webcam fallback forced a 4:3 stream to match the 4:3 plane. the camera
module defaults to 1280x720, so a 16:9 webcam was getting squished onto the 4:3
window. resize the feed plane and its border to the actual frame aspect (no-op
for a 4:3 feed).
the camera module streams 1280x720 by default, so starting the window at 16:9
avoids a visible resize the moment the first frame lands. applyAspect_ still
corrects to whatever the webcam actually delivers.
the dark frame was a solid plane behind the feed, so the passthrough backdrop
discarded the feed background only to reveal the dark plane. build the frame as
a rectangle with a feed-sized hole so the centre is empty and the real world
shows through.
the mask stores category ids and the shader thresholds id >= 0.5. linear
filtering interpolated between category ids, so a boundary against category 5
crossed the threshold much earlier than one against category 1, bleeding the
silhouette unevenly. nearest keeps category edges crisp and correct.
add a dispose() (the Script teardown hook) that removes the keydown listener and
releases the segmenter, textures, geometries and materials. also dispose the old
mask DataTexture before allocating a new one on a resolution change.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants