You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This commit was created on GitHub.com and signed with GitHub’s verified signature.
v4.53.0 — 2026-10-08
Added
The hand-written web viewer (sceneview.js) gains a transparent canvas, a camera API and render on demand (#4301).SceneView.create(canvas, { transparent: true }) clears to alpha 0 so the page shows behind the model. getCameraOrbit(), setCameraOrbit({ angle, height, radius, target, fov }) and frameModel({ fill }) replace poking private fields; the setters are chainable, and frameModel({ fill: 0.8 }) keeps the model within 80 % of the view, in height and in width, at every angle of the orbit. renderMode: 'onDemand' (or setRenderMode()) stops drawing once nothing moves and wakes on input, camera calls, scene changes or requestRender(). It shares the existing off-screen gate, so an idle or off-screen viewer draws no frames, video quads included. onFrame(callback) runs after every drawn frame while the canvas is still readable, for reflections or thumbnails. The defaults are unchanged.
SceneView(contentPadding = …): the subject stays in the area a panel leaves free (#4310). Declare the part of the surface a bottom sheet, a side panel or a title bar covers and the visible rectangle becomes the camera's viewport: the optical centre moves to its centre and the lens spans it. The surface is not resized, the camera pose and the user's orbit are not touched, and the mapping is linear in the padding, so animating it with the panel's own curve makes the subject follow the panel. A tenth of the view always stays visible, whatever the padding. Also on CameraNode.contentPadding, in pixels (ViewportPadding): a projection you set yourself (setProjection, orthographic, custom) is kept and only scaled and shifted, and autoFitContent fits again when the padding changes. Not applied in AR, where the projection is the device camera's. The Android twin of .contentInsets(_:) on iOS.
fitCameraToBounds / CameraNode.fitToBounds: frame any box under a given angle (#4310). Where fitDistanceForBounds answers "how far" for a subject that may turn, the new fit frames a bounding box as it is seen from one direction and also returns where to aim, so the picture is centred and touches the requested fill on the axis that binds. The box needs no model behind it: a board, a room or a whole scene declares its bounds as an Aabb. With a content padding set, the fit uses the visible area's aspect ratio.
iOS: SceneView.contentInsets(_:) keeps the subject in what a panel leaves visible (#4317). Pass how far your own sheet or side panel covers each edge and the visible rectangle becomes the camera's viewport: the subject is re-centred and scaled there and the view is not resized. With .autoCenterContent(false) the camera pose is not touched; with automatic framing on, the orbit distance follows the visible rectangle and a pinched or orbited camera keeps its zoom. It animates with the transaction that changes the insets, and a tap still lands on what is drawn, per-entity NodeGesture handlers included. 3D SceneView only, not ARSceneView; orbit, pan and first-person cameras. iOS twin of Android's SceneView(contentPadding = …).
Changed
The sceneview-web Kotlin/JS bundle tone-maps with Filmic, like Android (#4046). The view was left on Filament's default ACES (legacy) curve while Android sets Filmic (SceneFactories.createView), so one model was graded differently on the two platforms. Every scene rendered by the Kotlin/JS bundle (sceneview-web.js, npm sceneview-web) changes: saturated colours keep more of their saturation instead of washing out, at about the same brightness. The hand-written website-static/js/sceneview.js is aligned separately (#4347).
The pages under website-static/ no longer load the non-commercial Damaged Helmet (#4294). The home page hero, web.html, the playground, the platforms showcase and the view.html sample now load the Khronos Toy Car (CC0-1.0) as models/platforms/ToyCar.glb; the quickstart, the 3D Compose codelab, llms.txt and the sceneview-web agent skill point to it too. DamagedHelmet.glb (CC-BY-NC-4.0) leaves the web asset manifest and the credits page. The Toy Car is now credited as CC0-1.0 to Guido Odendahl and Eric Chadwick everywhere (it was listed as CC-BY-4.0 / "KhronosGroup"), and the AntiqueCamera.glb manifest licence is corrected to CC0-1.0. The web demo deployed at /web-demo/ (samples/web-demo/site) still ships its own copy of the helmet; it is removed separately. Part of #4158.
Website: foundation for the redesign (#4299). Adds the design tokens, self-hosted Geist (variable, 46 KB for latin and latin-ext) with a variable JetBrains Mono rule, and a 92-icon SVG sprite that will replace the 1.1 MB Material Symbols font. Current pages are unchanged.
Demo apps (Android and iOS): sample_open now sends the category as a stable slug (create, view_3d, place_ar, understand, dev_tools) plus the received entry_id and the umbrella card's mode; each mode change inside an umbrella card logs sample_interaction with control = "mode_<x>" (#4300). A mode opened by a link is reported by sample_open.mode instead of an extra sample_interaction, and the same card and mode use the same slug on both platforms. Demo-app instrumentation only; the SDKs still ship no telemetry.
Breaking: SceneView / Scene take a contentPadding parameter, and CameraNode.getViewPortAspect() follows it (#4310). The two composables' JVM signatures are replaced, so code compiled against an earlier version has to be recompiled; calls that pass their arguments by name compile unchanged. CameraNode.getViewPortAspect() now returns the aspect ratio of the visible area — the same value as before while no padding is set, the ratio of what the padding leaves free once one is.
Demo app: Rolling Balls runs edge to edge and keeps its board clear of the settings sheet (#4310). The stage now fills the screen under the title row and the controls, and the board is framed in the band they leave free. Open the settings sheet and the board slides up above it and stays whole — at its resting height and dragged all the way up — and a tap or a drag still lands the ball under the finger. In landscape the two rows of controls share one line and the board is centred over them, inside the window's safe insets, and the gesture hint steps aside whenever the free band is too short to hold both it and the board.
iOS: rotating the device keeps your zoom (#4317). A rotation or a resize used to hand an orbited or pinched camera back to the automatic fit. The orbit distance now follows the new shape of the view and comes back exactly when you rotate back.
The web demo at /web-demo/ no longer ships the non-commercial Damaged Helmet (#4318).samples/web-demo/site now bundles the Khronos Toy Car (CC0-1.0) as models/khronos_toy_car.glb in the Showcase list and opens on the Sheen Chair (CC0-1.0), which stays fluid on software-rendered WebGL where the Toy Car's transmission and clearcoat passes do not; khronos_damaged_helmet.glb (CC-BY-NC-4.0) is deleted and credits.json is regenerated. The recipes, the AR Compose codelab and the llms.txt samples load models/toy_car.glb, and the "runs on web" capture on the home page and in the README shows the Toy Car. Part of #4158.
The website's model viewers tone-map with Filmic, like Android and the Kotlin/JS bundle (#4347). The hand-written website-static/js/sceneview.js (site pages and /web-demo/) left its view on Filament's default ACES (legacy) curve. It now sets a Filmic ColorGrading, released with the viewer in dispose(): saturated colours keep more of their saturation and mid-tones are a little brighter. The background is no longer graded: the backgroundColor option and setBackgroundColor(r, g, b, a) now take the colour as displayed (sRGB 0–1, a hex byte / 255 — the contract sceneview-web already has), where they used to take linear values that ACES graded on the way to the screen. Every page of the site is restated so its background is the same pixel as before (default dark slate #333C57), except view.html and open/, which now show their stage-background design token instead of a darker grade of it. A page of your own that loads this script with explicit background values gets exactly those values on screen now — restate them if you had tuned them by eye. A transparent canvas is unchanged.
Android demo: 2D in 3D now has Inspect and Media modes. Tap a rocket part to restyle it from a live card attached to the object, or orbit a gallery of a picture, a video and camera-facing labels.
Android demo: a phone held sideways keeps the scene clear. In every demo the bands behind the title and behind the action bar covered more than half of the picture in landscape; the top one now fades out just under the status bar, the bottom one stops above the action bar, and the buttons, the title and the pills carry their own backing.
iOS and macOS demo: the App Store "What's New" text describes 4.53.0 (#4400). It names what changes on screen: the camera keeps its zoom through a rotation or a window resize, a model picked in Model Viewer lands at the right size, and the Cosmos loading hint stays readable.
Fixed
sceneview-web: camera { fov(…); near(…); far(…) } now reaches the projection (#3896). Explicit valid values override automatic projection and fit values independently, while unspecified or invalid clip planes remain content-derived. createViewerFull(fov) is now honoured too.
ViewNode: a drag that starts on an interactive card now orbits the camera (#4033). Any Compose container consumes the DOWN it is hit by, so a forwarded card used to keep the whole gesture and the camera never moved when a drag began on it. Once the pointer passes the touch slop (measured in the card's own pixels, so an enlarged card's list keeps its scroll), or a second finger comes down, and nothing inside the card has claimed the gesture (a scrolling list or a slider still does, through requestDisallowInterceptTouchEvent), the card gets an ACTION_CANCEL and the scene takes the stream over, with its DOWN replayed so the orbit starts without a jump; that handed-back drag never reads as a tap, a long press or a double tap, and it does not cancel a preceding scene tap still awaiting confirmation. Taps still click. SceneView and ARSceneView both behave this way.
ViewNode (lit): the corners around a rounded card are transparent again (#4032).view_texture_lit.mat used blending: transparent, which applies alpha to diffuse light only, so the transparent texels around a rounded Compose card still reflected the environment and showed as a dark rectangle. It now uses blending: fade; opaque pixels are unchanged. The blob is recompiled with the pinned matc 1.72.1.
Demo app, Picking & Collision: a picked shape is unmistakably lit (#4031). The highlight colour was also one of the shapes' default colours, so lighting that shape (and its near-twin) changed almost nothing on screen. A picked shape now turns amber (the existing warning accent token) and grows slightly.
Web Gaussian splats render as oriented ellipses with the right colours (#4046).sceneview-web now projects each splat's rotation and 3-axis scale to a screen-space ellipse with the same math as Android (pixel Jacobian, 0.3 px² low-pass, opacity-bounded extent), instead of a round billboard sized by the largest axis, and undoes the view's Filmic colour transform so a splat displays the colour it stores. A cloud above 2^24 splats is refused with the limit in the error (onError / IllegalArgumentException) before anything is allocated, instead of a generic fetch failure.
iOS demo: the Cosmos "Lighting up the cosmos" hint stays readable over the lit star (#4256). The hint now sits on the chrome-scrim token (white on 60 % black, at least 5.7:1 over any ground).
iOS demo: the macOS build compiles again (#4303). The Mac App Store upload of v4.52.0 failed on two iOS-only calls in the demo app (the Secondary Camera backdrop colour and the mode picker's haptic). macOS now uses Color(nsColor:) and skips the haptic; iOS is unchanged.
Demo app: the top of the Home shows each sample once, and "What's new" no longer hides the catalogue (#4304). On Android the same sample could lead the header pager, the "What's new" row and the "Featured" banners one after the other. A sample is now pushed once above the catalogue (pager, then Featured, then What's new): the pager keeps Models and Cosmos, the Featured banners keep AR placement, AR Rerun and Materials, and the full list does not repeat a banner under it. Tapping the "What's new" row opens the "What's new" sheet over the full catalogue instead of selecting the filter, which made the app look like it only had those samples. The filter chips stay for who wants the catalogue cut down, and a filter now always shows its way out: tapping the selected chip again goes back to "All", Back clears the filter before it leaves the app, and a filtered list ends on a "Show all N samples" button.
iOS demo: freshness badges now match the demos that visibly changed in 4.52.0 (#4305). Contact Shadow Preview, Depth Visualization and Secondary Camera are marked as new, while AR Placement, Body Tracking, Camera & Gestures, Cosmos, Depth Collider, Materials, Model Viewer, Rolling Balls and Scene Mesh are marked as updated; unchanged demos no longer receive an "Updated" badge.
Picking under a shifted or scaled projection (#4310).Camera.viewToRay, viewToWorld, worldToView and View.screenToRay read Filament's projection getters, which leave setScaling and setShift out: a touch on a subject drawn off-centre was answered for where it would have been without the shift. They now go through the projection that is drawn (Camera.renderedProjectionTransform).
Demo app: demos that frame their scene between the title and the controls render in landscape (#4310). On a phone held sideways the two bands were taller than the window: the scene was laid out with no height, never drew a frame, and the loading cover ended on "stalled". When less than 160 dp would be left, the scene now stays full-frame under the controls.
addLight() no longer throws for point and spot lights in the web viewer (#4316).sceneview.js called Filament.mat4.translation(), which the Filament.js binding does not have, on a light entity with no transform component. It now creates the component and sets a column-major translation. Lights added this way are tracked, so removeLight() and removeNode() both remove the light and free its entity; removeLight() used to leave it allocated.
The web demo runs the same viewer as sceneview.dev (#4316).samples/web-demo shipped its own copy of sceneview.js, stuck at 3.6. That copy is gone: the deployed /web-demo/ route, the Playwright server and local runs all stage website-static/js/sceneview.js. The demo passes its relative IBL URL explicitly, and its "Remove Added Lights" and "Remove Added Text" buttons, which silently did nothing, now remove what was added.
Models authored in millimetres are no longer culled by the web viewer's far plane (#4316). The far plane was a fixed 1000 units; it now follows the zoom-out limit of the framed model once that exceeds 1000, so the demo's Retro Piano renders. Nothing changes for a model framed closer than about 133 units.
iOS demo: a model picked while its sheet closes lands at the right size (#4317). The Model Viewer's entrance flight could start from a fit measured while the Models picker was still sliding away, and leave a wide model framed for half the screen. The entrance now waits for the sheet to settle, and a sheet that moves under a flight lands it at once. The demo sheets inset the scene only where they sit on the bottom edge: iPhone, and iPad from iPadOS 27.
Demo app: environment changes no longer flash black (#4319). Android keeps the current skybox and image-based lighting visible until the selected replacement is ready, then swaps both together; rapid selections discard superseded loads without retaining their Filament resources. In Materials, the picked environment chip shows a loading mark until that environment is on screen.
rememberHDREnvironment and rememberKTXEnvironment keep the current environment while the next one loads (#4319).EnvironmentLoader.destroyEnvironment now matches by identity, also frees the HDR cubemap textures the loader created, and still destroys environments it never registered.
Android demo: the cards changed in 4.52.0 say so (#4320). Thirteen demos that gained a mode, a reworked screen or a visible fix in 4.52.0 (Cosmos, Rolling Balls, Camera & Gestures, AR Placement, Cloud Anchors, Geospatial Anchors, Depth Visualization, AR Measure, Plane Node, Plane Renderer V2, Scene Geometry, Splat Room and Rerun) now declare updatedIn = "4.52.0". Eight of them declared 4.51.0 and would have lost their "Updated" chip one release early, Splat Room declared 4.49.0 and Depth Visualization, AR Measure, Plane Node and Scene Geometry declared no update, so those five carried no chip at all. A "Coming soon" card is never marked, as on iOS: the headset-only Android XR card no longer reads "Updated".
Demo app: three demos keep their subject clear of the controls in landscape (#4326). On a phone held sideways, Double Pendulum swung behind the Release button, the Model Viewer's model sat half behind the dock and Contact Shadow's scene ran under its controls. The three now hand what covers the scene — the chrome, the settings sheet where it is, the display cutout — to contentPadding, and fit their camera for the area left free at rest: the subject is drawn in that area and follows the sheet up, and the orbit the user set survives opening Settings. The pendulum's fit now includes the tip bob — upright it is drawn about 9 % smaller for it — and, in landscape, runs from the stand's foot to the highest the swing can climb rather than to the top of the reachable disc. In landscape Contact Shadow stands further back behind a longer lens, so the TV, its shadow and the two boxes share the band above the controls. The viewer's Lighting sheet reports the half of the window it rests at in landscape, not its whole size, so the model stays above it instead of shrinking away.
Android demo: the Geometry scene shows the seven primitives and is usable in landscape (#4335). Geometry Primitives drew four of the seven built-in shapes on a black background, re-centred the others each time one was hidden, and in landscape put its subject under the chips row. It now gives CubeNode, SphereNode, CylinderNode, ConeNode, TorusNode, CapsuleNode and PlaneNode a fixed slot each on the stage, in tones that read against it and under a studio light, fitted to the band between the title and the chips with contentPadding and fitCameraToBounds: a 2-3-2 block in portrait, a single row on a landscape phone, through a lens that lengthens with the band so the row is not stretched. Hiding a shape moves nothing, the spin can be paused, Recenter and Reset bring the camera home (also while paused), and at Metallic 1 with Roughness 0 the shapes mirror a studio instead of an empty scene (the round ones catch its lights; a flat face still goes dark while it faces a dark part of it). The shape chips are a legend — a dot of the shape's colour, a hollow ring and a struck-through name once hidden — drawn by a glass chip Rolling Balls now shares. The Showcase card is a capture of the scene.
Collaborative AR: a placement is no longer lost, and both devices agree on a node (#4382).CollaborativeSession sent everything through one conflated queue, so a placeNode, the Cloud Anchor id or the hello could be replaced by the next line before it left the device. Only camera poses are conflated now. Inbound messages are merged one at a time, in arrival order, instead of concurrently. And calling placeNode on a key a peer placed first now shows on the caller's device too — it used to keep the peer's old copy while every other device got the new one. No API or wire-format change.
AR on Android: the display no longer sleeps in the middle of a session (#4392).ARSceneView keeps the screen on while it is composed and hands the previous setting back when it leaves — a scan, a Streetscape wait or a Cloud Anchor hosting is no longer cut by the display timeout. No API change; a 3D SceneView is unaffected.
Android demo: the room scan no longer shows a "Session MP4" switch. The pill that sat above the action bar of Rerun AR Replay — on its landing, its replay and its live camera — is gone, so the demo opens straight on the room scan and nothing covers the scene. The ARCore session recorder behind it is a developer tool and stays reachable by deep link (sceneview://demo/ar-rerun?tab=session-mp4).
Performance
Web splat re-sorts upload one small texture instead of two (#4046). Attributes are uploaded once in cloud order; a re-sort only rewrites the order texture, now 4 bytes per splat (0.94 MB instead of 7.5 MB for the 233 808-splat raccoon scan).
Tests
CI render set: the Geometry reference pictures the rebuilt scene (#4352).render-goldens-swangle/geometry_default.png still showed the four-shape scene the Geometry rebuild replaced, so the advisory demo-render-goldens job would have reported a diff on every run. It is re-recorded from a reviewed CI capture of the seven-primitive scene.
Docs
The migration guide points 2.x apps to their section (#4398). The guide opens on the newest release; it now says so and links straight to the 2.x section.