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.44.0 — 2026-09-27
Fixed
setup-ar-emulator.sh --seed-snapshot refuses to save a snapshot without ARCore (#2749). A failed ARCore install was swallowed by an install_arcore && log AND-list, which errexit ignores, so the seed saved an ARCore-less qa-clean snapshot and exited 0. The seed now re-reads the package list and exits 1 when com.google.ar.core is missing, and a failed install is logged as a warning on every run.
The playground's iOS snippets now compile against SceneViewSwift (#3192). All fourteen used APIs that do not exist (ModelNode("…"), DirectionalLightNode, .scaleToFit, .onTapAR, .onFaceUpdate). They now load models with ModelNode.load, light with LightNode.directional, place AR content with onTapOnPlane and AnchorNode.world, and the "copy prompt" iOS block says the same thing. sceneview.js reported 4.18.0 in its header and in SceneView.version; both now read the current version and sync-versions.sh keeps them there. The structured-data FAQ no longer advertises the removed list_node_types MCP tool, and the GPT system prompt's version block reads the current release and Kotlin 2.4.20.
Demo — Open with SceneView: an STL, OBJ or PLY shared as a nameless application/octet-stream now opens instead of being refused. The staged file is recognised
from its own bytes with the SDK's StlLoader.isStl / ObjLoader.isObj / PlyLoader.isPly, as
GLB, glTF and 3MF already were, and a name with no model extension gets the one the bytes
proved, so the real-size question still appears for these unit-less formats (#3490).
The README and docs format tables now say what Apple actually opens (#3655). The README matrix claimed glTF / GLB on Apple, which RealityKit does not read and SceneViewSwift's ModelFormat excludes on purpose, and denied STL, OBJ, PLY and 3MF, which Apple has opened since #3513. The README, the Model formats page and the iOS section of the Platforms page now agree with ModelFormat.swift: USDZ / Reality / USD through RealityKit, STL / OBJ / PLY through ModelIO, 3MF through SceneViewSwift's own parser, no glTF (convert with tools/convert-usdz.sh).
Demo app: the AR Pose coordinate readout uses the glass chrome tokens (#3683). The pill over the camera feed hardcoded Color(0xCC161B22) with a comment naming a SurfaceDim token that has since moved twice. It now takes SceneViewTokens.Glass — the chrome-scrim fill, on-glass text and the over-media-edge boundary — like the rest of the over-media chrome.
Shared links to /open/, /preview/, /embed/ and /web-demo/ now unfurl (#3688). The four pages most often pasted into a chat or a ticket had no Open Graph or Twitter metadata, so Slack, Discord and GitHub showed a bare URL. They now carry the same block as the marketing pages (title, description, 1200×630 og-image.png, summary_large_image). /open/, /preview/ and /embed/ deliberately omit og:url: they are addressed by query string, and a bare canonical URL would make crawlers unfurl the empty page instead of the shared model. geometry-demo.html pointed its og:image at an SVG favicon, which crawlers do not render; it now uses the PNG card.
Website text now reads at 4.5:1 or better in light mode (#3689). The hero's "Try the demo:" label used a surface token on the blue gradient. It is now white at the hero description's 0.9, which gives 5.5:1 on both gradient stops. "Stable" and "Fully supported" labels use a darker green in light mode (#166534), which holds 5.6:1 or better on their tinted chips. --color-outline is #56688f in light mode, so the playground sidebar headers, counts and line numbers that use it as text pass on every surface tier. Captions and API signatures no longer dim the muted token with opacity. Code annotations in the playground are #b45309. The "v4" badges in the developer-tools section pointed at an undefined --accent, so they rendered white on white; they now use the primary colour. A DOM audit of seven pages found 85 failing text nodes in light mode before this change and 0 after. Dark mode went from 7 to 0.
A web viewer below the fold now holds a finished frame of its model instead of an empty canvas or a black silhouette (#3690). The render loop parks while a canvas is off-screen (#2508), and gltfio decodes a model's textures asynchronously after the geometry is in the scene. On /web.html the demo loads off-screen, so the loop parked before drawing the model at all, or right after drawing it untextured. A reader who scrolled down was fine, because the loop resumed. A full-page capture, a print or any other off-screen read of the canvas was not. The viewer now draws one frame while parked: when the textures finish decoding, when the IBL lands and after a resize. It retries on the next animation frames if Filament skips that frame. The loop itself still stays parked off-screen.
Demo — Materials: leaving Inspect a few seconds after dragging the camera no longer cuts to
the hand-over pose. The framing the user left rides the flight back to the wall and eases away
across it, so the first frame of the flight out shows the last frame of Inspect (#3692).
Demo — accessibility: the shared LabeledSlider no longer erases its track's semantics.
TalkBack now announces every demo slider as an adjustable slider (swipe up / down works), a
disabled one as disabled, and still reads the value once, in the unit on screen (#3721).
Two views rendering one shared Filament Scene both wake on node changes under FrameRatePolicy.OnDemand (#3723). The scene → view registry kept one entry per scene, so the second SceneView (or an ARSceneView next to a SceneView) to compose silently overwrote the first: a node change woke only the second view while the first stayed frozen, and when either view left composition it removed the shared entry, so the surviving view stopped waking entirely. Each view now registers alongside the others, a node change wakes all of them, and a view leaving composition removes only its own entry. The registry still holds scenes weakly, so a destroyed scene never retains a view.
Demo app: a selected dock toggle is legible over bright scenes (#3727). A selected item used to tint its icon and caption pale blue on the dock's glass, which over the near-white Contact Shadow studio fell to about 3:1. It now sits on a filled dock-selected pill (the dark-scheme primary, with a dark foreground) that reads at 7.3:1 on any scene, in every demo. The Contact Shadow dock caption "Shadows" is now a string resource.
Demo app: Contact Shadow "Reset demo" sends the camera home and keeps your Bounce motion choice (#3728). The reset used to leave the camera wherever it had been orbited and silently turn Bounce motion back on. The orbit is now rebuilt at its authored home on reset and eased there through the demo's continuity layer (the Model Viewer's recenter pattern, a cut in QA mode), and Bounce motion stays as the user set it. The reset lives in a small state holder covered by ContactShadowDemoResetTest.
Demo — Wall placement: the rotate buttons in the adjustments sheet now read "Rotate
clockwise" / "Rotate counter-clockwise", which is what they do — wall content spins about the
wall's normal, never left or right. The floor feature comparison keeps "Rotate left / right"
on strings of its own (#3741).
The TerrainAnchorNode / RooftopAnchorNode samples now compile and resolve once instead of every frame (#3908). The KDoc sample passed earth = to resolve(), which takes session, and started a new billed Geospatial request on every frame until the first one succeeded. The KDoc and llms.txt samples now keep the returned future in remembered state (the guard that stops a second request), cancel it on dispose, hop to the main thread in the callback, destroy a node that resolves after the screen was left, and hand the node to the TerrainAnchorNode(node = …) / RooftopAnchorNode(node = …) composables. Both llms.txt snippets are compiled by the snippets check.
TV demo: picking a model now shows a loading state — a progress ring and the model's name, sized for ten feet — until the model's first frame is on screen, instead of a black stage for up to 13 s. The previous model is no longer left on screen under the new model's name while the next one loads. (#3926)
A minor release in which no demo changed no longer fails DemoFreshnessTest (#3927). The test required at least one demo to carry a "New" / "Updated" marker for the build's version, so every version-bump PR where no demo PR had declared updatedIn went red inside the release run, after the QA gate. "Nothing is fresh" is now a legal state: the Showcase draws no chip and the What's-new sheet drops its "to try" section, as it already did. The upper bound stays — no more than a third of the grid may be marked.
Leaving a scene while its model is still loading no longer freezes the app (#3981). Disposing rememberModelInstance or rememberModelLoader mid-load called gltfio's asyncCancelLoad on the main thread, which joins the running texture decoders and then waits for the whole GPU backlog — an ANR on slow devices. The model is now claimed at once and freed from a main-looper poll once its load has settled (decoding done and backend idle, or no progress for 10 s), the loader's native resources wait for those destroys, and the engine's deferred destroy waits for them in turn instead of forcing them.
A render-on-demand scene no longer parks on a single frame when the GPU is slow (#3982). The settle window after a change closed on the first presented frame past its 500 ms deadline, and while Filament links a model's materials that first frame is often the only one presented in the window. The scene then froze on it, and the demo's "Still loading…" card, which waits for a second frame, stayed up over a finished model (Model Viewer) or a black viewport (2D in 3D, Scene to MP4) until a touch woke the loop. The window now also owes at least two presented frames after each change, and Model Viewer asks for the frame its cover still needs once the model has loaded.
Demo app: no more grey bands around inset scenes (#3983). Double Pendulum, Geometry Primitives, Animation & Physics, Contact Shadow Preview, 2D in 3D and Scene to MP4 shrink their viewport to keep it clear of their controls. The space above and below it showed the window background, light grey under the scrims in both themes. It is now painted with the dark stage colour, so the controls sit on one continuous stage.
Demo app: the status bar stays readable over the dark 3D stage in light theme (#3984). Opening Model Viewer, Materials, Lighting and most other demos from the Showcase left dark status-bar icons on the dark stage, so the clock and battery disappeared. The Showcase and the demo each saved, forced and restored the icon colour, and the screen that left last restored the wrong one. Screens now only ask for light icons, and one place at the root applies the result, so navigation order no longer matters. In dark theme, the online gallery's "View in 3D" viewer also gets light icons instead of dark ones.
Demo app: floating controls fade out under an open sheet, like the dock (#3985). Model Viewer's "Surprise me" pill stayed visible through the Lighting and Settings sheets, right on their sliders, where it read as a live button inside the sheet. Every demo's bottom overlay now fades with the dock while a glass sheet is open and comes back when it closes.
AutoPlacementScene no longer draws its coaching glyph over the "Couldn't start AR" card (#3986). Both are centred, so when ARCore could not start a session the phone-sweep disc kept covering the card's explanation and most of its Try again button. The coaching overlay now stays silent while ARSceneView shows its availability card. The demo app's AR Placement, AR View and feature-comparison screens get the same fix.
Demo app: AR Placement's streamed models show their own picture (#3987). The six "Streamed" cards showed the thumbnail of their bundled stand-in, which is a different model: Coffee Mug showed the Olive Dish, Picture Frame showed the Soldier. Each streamed card now shows its own Sketchfab thumbnail, or the generic AR glyph when there is no API key or no network. The card title also no longer shares the "Streamed" caption's grey, so the caption reads as a caption.
Demo app: the Point & Ask cards are readable in dark theme again (#3988). The "Gemini Nano isn't available" card, the failure card and the answer card drew near-black text on a dark card: their translucent surface is not an exact scheme colour, so Material could not pick a content colour for it. The card now sets onSurface explicitly.
Demo app: Rerun Debug no longer stacks its preview tile on the "Couldn't start AR" card (#3989). When AR cannot start, the 3D preview tile covered the right half of the card's title and the "No computer connected" card sat above it. While that card explains the failure, it is now the only card on screen; the tile and the connection card come back as soon as a session starts.
Demo app: Geospatial Anchors offers a way forward when a permission is denied (#3990). Answering "Don't allow" left a line of text on a white screen, with Back as the only exit. The screen is now the dark stage with a card that says which permission is off and why the demo needs it, and one button: Allow while Android can still ask, Open settings once it cannot. Coming back from Settings starts the demo. Plane Renderer V2 and every other demo whose AR session fails also keep the dark stage instead of turning white.
Demo app: AR View runs edge to edge after Start AR Camera (#3991). The session hides the status bar, but the tab host still padded it down by the display cutout, so a band of window background ran across the top of the camera: white in light theme, near-black in dark. The host now drops its insets during a live session, and the AR screen's own controls keep clear of the cutout.
Demo app: What's new lists only changes you can see in the app (#3992). The sheet used to show every line of the project changelog, including SDK, iOS, release-tooling and QA notes. It now shows only the entries written for demo users (the ones that open with "Demo app:"), without that prefix. The newest block is titled "New in this update" instead of "Included in this preview build".
Docs
Node.rotation and Node.worldRotation document their Euler conventions (#3745).rotation is ZYX and reads back what was written. The worldRotation getter is YXZ with the yaw sign negated, even on a node with no parent, while its setter is ZYX. A pinning test records this; behaviour is unchanged because major version 4 is frozen. Compare or copy orientations through quaternion / worldQuaternion.