Spatial Computing • visionOS • 25:32
Discover how to play immersive videos in visionOS apps. We’ll cover various immersive rendering modes, review the frameworks that support them, and walk through how to render immersive video in your app. To get the most out of this video, we recommend first watching “Explore video experiences for visionOS” from WWDC25.
Speakers: Jamal Yauhari Martinez, Michael Weinstein
Downloads from Apple
Transcript
Introduction
Hi! I'm Jamal, and I’m a media applications engineer in the AVKit team. And I’m Michael, a software engineer on the visionOS Spatial Media team. One of my favorite things to do on visionOS is watch videos in a way that is unique to the platform. In visionOS 2, that includes amazing experiences like docked playback and spatial videos. On visionOS 26, we’ve expanded all your favorite media frameworks, such as Quick Look, AVKit, and RealityKit, to support more immersive media profiles, and to help you create a great, immersive video playback experience in visionOS.
Today, Jamal and I will share how to support this immersive video playback in your application. Take it away, Jamal. Thanks, Michael. First, I’ll briefly review the different types of video profiles. Then, I will go over how Quick Look, and new APIs in AVKit support immersive media playback. Finally, Michael will go over how applications can customize immersive playback experiences with RealityKit. By the end of this video, you will learn everything you need to know, to support and create an immersive video playback experience, in your visionOS application.
Video profiles supported in visionOS 26
I’ll start by reviewing the different types of video profiles supported in visionOS 26. In visionOS 1, 2D and 3D video were the main way of offering video playback in applications. Spatial media enabled people to shoot compelling stereo content and enjoy their creation in an immersive way. visionOS 26, now includes Apple Projected Media Profile or APMP, for 180, 360, and wide field-of-view videos. And for the ultimate immersive experience, there is Apple Immersive Video.
Each of these profiles are unique in their own way. If you're unfamiliar with the terms, the “Explore video experiences for visionOS” session, will have you covered. There are many ways to support all the video profiles I just listed. For this, choosing the right technology for your application, is important for offering an exceptional immersive experience.
Quick Look, is an ideal framework for quickly presenting any kind of media, including immersive media. AVKit, provides a familiar and consistent video experience on every platform, while offering enhanced controls for the playback experience. RealityKit, is designed for applications that demand a one-of-a-kind immersive playback experience, like those found in video game environments. Lastly, if you are looking for immersive playback support in a browser. Check out our session “What's new for spatial web”, for a more detailed explanation about immersive playback for web content.
Next, I’ll review the tools available for Quick Look and AVKit in visionOS 26, so you can go ahead and get started creating your immersive video playback application Quick Look offers two APIs for quickly displaying and previewing media in apps.
Immersive video playback in Quick Look
And QLPreviewController, an API for previewing media within an app’s window or in a modal presentation style. in visionOS 26, QLPreviewController, has been enhanced to support spatial photos and videos. Alongside this, PreviewApplication now supports Apple Immersive Video, and Apple Projected Media Profile, including 180, 360, and wide field-of-view videos.
QLPreviewController and PreviewApplication can manage the presentation and transitions of new video profiles. They also adapt their preview with the appropriate video styling. In visionOS 26, all applications already implementing Quick Look APIs, will automatically support immersive media profiles. To learn more about how to implement the PreviewApplication or QLPreviewController APIs, refer to our videos “Discover Quick Look for spatial computing” from WWDC23 and “What’s new in Quick Look for visionOS” from WWDC24 Next, I’ll go over new AVKit APIs, which we introduced to fully support immersive video playback.
Immersive video playback with AVKit
In visionOS 26, The AVExperienceController API is leveraged to achieve transitions into a new immersive experience. There are various options to achieve transitioning into an immersive experience with AVExperienceController. The first option being, with Expanded. The Expanded experience allows AVPlayerViewController to consume the entire UI window Scene, And now in visionOS 26, Expanded is configurable to achieve immersive video playback.
New in the Expanded configuration, the AutomaticTransitionToImmersive property is used to determine if automatic transitions to an immersive experience should be initiated. The AutomaticTransitionToImmersive property can be set to a value of either default for when the system’s default behavior is preferred or none for when no automatic transitions are desired.
Setting none as the value for the Automatic Transition To Immersive property property gives the AVPlayerViewController a portal treatment for immersive content when detected. While keeping AVExperienceController in the Expanded experience. Here is an example for disabling automatic transitions to an immersive experience, for when portal treatment for immersive content is desired.
I’ll first create an AVPlayerViewController and set it up with the immersive media content. Then, I will add to its experienceController the recommended set, which includes the appropriate experiences for the platform, while ensuring Expanded, and Immersive is part of it. Given that the AutomaticTransitionToImmersive property is initially default, I will now need to specify the value of the property to be .none.
Assuming that AVPlayerViewController is already in the applications view hierarchy, I'm ready to transition the AVExperienceController into the Expanded experience. With the new Immersive API, as part of the Experiences in AVExperienceController, it is possible to transitions explicitly into this experience, rather than just relying on an automatic trigger. Just like its done for Expanded.
The Immersive experience API comes with a new configuration API, that allows apps to define the placement of where immersive video playback should be experienced. This code snippet demonstrates how to use the Immersive experience with the Configuration API. Assuming that the AVPlayerViewController has not been added to the view hierarchy. You will need to specify this through the Configuration API.
Do this by accessing the Experience Controller Configuration, and provide the placement value with the .over function for your preferred window UI Scene. Once defined, AVExperienceController is ready to transition into an Immersive experience. If the AVPlayerViewController is already contained in the view hierarchy, AVExperienceController will assume that the window scene where it is contained, is the desired placement scene. And that's how applications are able to transition into an Immersive experience with AVKit. When transitioning in and out of the Immersive experience, AVExperienceController handles the animations and transitions between experiences. These transitions are initiated at any moment, either by the user, the application logic, or by the system.
Therefore, when using AVExperienceController it is important to understand any transition or presentation state changes, this will give you the flexibility to appropriately handle your application active state. For this, AVExperienceController’s Delegate Protocol is the solution. The protocol has 3 delegate methods: didChangeAvailableExperiences, which notifies when the possible available experiences have changed.
prepareForTransitionUsing, which notifies when AVExperienceController is about to transition, enabling your app to prepare for the new state one last time. and didChangeTransitionContext, which notifies when the transition into the new experience has finalized. Immersive as an experience, is dependent on the type of content provided. Use the didChangeAvailableExperiences method, to determine whether an immersive experience is available for the current content type. For example, if 2D media content is provided to the AVPlayerViewController, the Immersive experience will not be available. To learn more about how to use AVExperienceController and its delegate methods, check out the “Playing immersive media with AVKit“ sample code.
Comfort mitigation detection
Additional to the new APIs for immersive video playback, Quick Look and AVKit now support comfort mitigation detection in visionOS 26. Immersive videos can exhibit substantial camera motion, which could lead to viewers discomfort. To address the issue, motion detection is now supported for Apple Projected Media Profile content. Now, QuickLook and AVKit, detect high motion during playback and automatically reduce immersion level. The viewer can adjust the options in the Settings app, allowing for Quick Look and AVKit to behave exactly how the viewer wants.
Custom playback in RealityKit
For most immersive video playback experiences, Quick Look and AVKit are great! For a more custom immersive experience, RealityKit is the ideal choice, and I’ll let Michael tell you why. Thanks, Jamal. And I agree RealityKit is a great framework for custom video playback, like including video in an immersive game, or rendering video with a custom user interface. And in visionOS 26, RealityKit supports native playback of immersive videos.
In this section, I’ll review a new progressive immersive mode for 180, 360 and wide field-of-view videos, and Apple Immersive Video, And spatial video rendering with full spatial styling, just like the Photos app. I’ll also demonstrate how to detect when video comfort mitigations are applied. And finally, I’ll share some tips about using RealityKit for video playback in a SwiftUI scene. VideoPlayerComponent is a powerful API for rendering videos in RealityKit. When attached to a RealityKit entity, it creates its own mesh and material, based on the current video in the AVPlayer, and properties on the component.
This makes VideoPlayerComponent ideal for rendering videos with immersive viewing modes, because mesh updates and animations are handled automatically, like in this example of video docking from the Destination Video project on developer.apple.com. Check out “Enhance your spatial computing app with RealityKit” for an introduction to VideoPlayerComponent. In visionOS 26, VideoPlayerComponent supports all of the same immersive video profiles that Quick Look and AVKit now support.
Progressive immersion mode in RealityKit
I’ll start with Apple Projected Media Profile videos, and Apple Immersive Video. VideoPlayerComponent supports three immersive modes for these video profiles. Portal mode renders the video in a portal, for a windowed presentation. Progressive mode is new API that allows people to dial in their own immersion level using the digital crown, allowing them to stay connected to the world around them, while continuing to enjoy the video. And at 100% immersion, progressive mode is equivalent to the full immersive viewing mode.
Starting with visionOS 26, progressive immersive viewing mode is preferred over full immersive viewing mode for Apple Projected Media Profile videos, and Apple Immersive Video, because of the additional flexibility it provides, and also to support comfort mitigation, which I’ll cover in more detail later. Here, I’ll make a view to render a 180-degree video in portal mode, and I’ll place it in a WindowGroup in the shared space.
To configure portal playback, first, create an AVPlayerItem and AVPlayer, with either a local or HTTP Live Streaming URL. Then, initialize a VideoPlayerComponent with the player. Set the component’s desiredImmersiveViewingMode to portal, and attach the component to an entity. VideoPlayerComponent’s mesh has a 1 meter height by default. I scale the video to .4 meters, to fit inside the SwiftUI window scene. Finally, add the entity to the scene. To render this video in progressive mode instead, change the component’s desiredImmersiveViewingMode from portal to progressive. Since the component controls scale in progressive mode, scale operations have no effect. I’ll remove it to keep things clear.
But updating the component to progressive is not enough, when rendering in a SwiftUI scene. To avoid clipping against window scene bounds when the mesh expands, I have to put the new ProgressiveVideoView in an ImmersiveSpace. And when rendering with progressive mode, that ImmersiveSpace must have a progressive immersion style.
Here, the initial immersion level is 1: equivalent to full immersion. I chose a wide range of 10% to 100%, so people can dial their own immersion level. If I had included 0%, immersion could be dialed down until the content disappeared, which is not the behavior I want for my app.
Both SwiftUI’s ImmersionStyle and the component’s immersive viewing mode are important for configuring playback. Always match desiredImmersiveViewingMode with ImmersionStyle, when rendering in a RealityView. For more information about Immersion Styles, check out the 2024 video “Dive deep into volumes and immersive spaces” To transition between portal and progressive modes, wait for the ImmersiveViewingModeDidChange event when switching SwiftUI scenes. ImmersiveViewingModeWillTransition and DidTransition events signal when to toggle UI visibility during animated transitions, to reduce motionand stereo conflicts. For examples of these events in action, look through the Playing immersive media with RealityKit sample code, on developer.apple.com.
To review: for portal rendering for Apple Projected Media Profile videos, and Apple Immersive Video, set desiredImmersiveViewingMode to portal. Portals are typically in a SwiftUI WindowGroup in the shared space, but can also be exclusive in an immersive space with a mixed immersion style. For immersive rendering, set desiredImmersiveViewingMode to progressive, in an ImmersiveSpace with a progressive ImmersionStyle like my code earlier. To get preferred system behavior, don’t set desiredViewingMode. viewingMode will automatically be mono for monoscopic videos, and stereo for stereo videos such as stereo 180 and Apple Immersive video.
Spatial video rendering with RealityKit
Spatial videos are stereo videos that people like maybe you have already been capturing across the Apple ecosystem. They contain spatial metadata that enable more comfortable and immersive rendering. Just like Apple Projected Media Profile videos and Apple Immersive Video, spatial videos are now natively supported in RealityKit, and render with full spatial styling and immersive modes.
Spatial styling for spatial videos is configured with the desiredSpatialVideoMode property on VideoPlayerComponent. Set this property to specify how a spatial video should be rendered. Read the get-only spatialVideoMode property to determine how a spatial video is being rendered. To opt in to spatial styling for spatial videos, set desiredSpatialVideoMode to .spatial.
Spatial rendering supports both .portal and .full ImmersiveViewingModes. Unlike other immersive video types, spatial video immersive rendering is always configured with an immersive viewing mode of full. Immersive spatial videos render at a fixed size based on the field-of-view of the content. Set desiredSpatialVideoMode to .screen, the default value, to render spatial videos in traditional stereo on a screen mesh.
SpatialVideoMode will not update unless the current video is a valid spatial video. As the name suggests, this mode only applies to spatial videos. Subscribe to the new VideoPlayerEvent: SpatialVideoModeDidChange, or directly observe the`spatialVideoMode` property, to determine if and when the spatialVideoMode property has updated, To render as patial video in portal mode, first create a VideoPlayerComponent with an AVPlayer that holds a spatial video. Set the desiredViewingMode to stereo. This is default for spatial videos, but I like to be explicit. Then set the desiredSpatialVideoMode to spatial, and choose portal as desiredImmersiveViewingMode. Like earlier, scale the video to fit inside the window.
When in spatial mode, the video can be expanded by setting immersive viewing mode to full. I’ll also remove the scaling operation, since the component controls size during immersive presentation. Like in my earlier example, the view will need to be in an ImmersiveSpace to avoid clipping. But immersive spatial video rendering is not headlocked like other immersive video types, so I’ll need to set the entity’s position. I’ll choose a meter and a half above the floor, and a meter forward.
But for a more robust solution, use a head anchor to initialize the entity with a position in front of the viewer. And finally, I’ll wrap this ImmersiveSpatialVideoView in an ImmersiveSpace. For spatial videos, use a mixed ImmersionStyle, to render immersive mode over passthrough. To allow spatial videos to also render in system environments, which is behavior I always want, use the new immersiveEnvironmentBehavior scene modifier with the coexist option. To review, spatial video portals are configured with a desiredSpatialVideoMode of spatial, and a desiredImmersiveViewingMode of portal, in either the shared space or an ImmersiveSpace with mixed ImmersionStyle. Viewing mode will default to stereo for spatial videos.
Spatial video immersive mode is set with a desiredImmersiveViewingMode of full and an ImmersiveSpace with a mixed ImmersionStyle, to render over passthrough. To render as traditional stereo, without spatial styling, set desiredSpatialVideoMode to screen. Immersive viewing modes do not have an effect in this mode. Spatial videos can also render monoscopically with a desiredViewingMode of mono. In this mode, neither SpatialVideoMode nor ImmersiveViewingMode have an effect. These non-immersive modes are typically in a WindowGroup, in the shared space.
Comfort mitigation detection in RealityKit
Watching immersive videos is an incredibly, well, immersive experience. This means playback can be super sensitive to high motion in the video. So for Apple Projected Media Profile videos, RealityKit automatically performs comfort mitigations during playback, like Jamal described earlier for AVKit and Quick Look. The new VideoComfortMitigationDidOccur event signals when a comfort mitigation is applied by the system, in response to high motion in the video. No action is needed upon receiving this event, it’s simply a signal that a certain mitigation has been applied.
The reducedImmersion mitigation is only available during progressive rendering That’s why it’s important to use a progressive immersive viewing mode and immersion style, instead of full, for Apple Projected Media Profile videos. During portal rendering, no mitigations occur, as portal playback is already comfortable for most content. The supported behaviors and rendering styles of a VideoPlayerComponent depend on the specific video profile being presented. Use the ContentTypeDidChange event to detect the kind of video in a VideoPlayerComponent, including new types like Apple Projected Media Profile videos. React whenever the video type changes: to understand what viewing modes are available, if comfort mitigation will be applied, or to update UI elements.
RealityKit content integration with SwiftUI
When VideoPlayerComponent is combined with UI, or just presented in a SwiftUI scene, I have some tips for smooth integration between RealityKit content and SwiftUI. For example, managing the scale of a mesh is important for placing media alongside UI. In portal mode, the mesh size is reflected by the playerScreenSize property, in the entity’s local coordinate space.
The portal mesh is always created with a height of 1 meter. When scaling a video entity, always scale X and Y uniformly, to maintain aspect ratio. If the window scene has a height smaller than 1 meter, the mesh will be clipped by the scene bounds, unless the entity is scaled down. To scale a video to fit inside the scene, set the scale based on window size available using GeometryReader3D Refer back to the sample project “Playing immersive media with RealityKit” on developer.apple.com for an example of scaling a video portal to fit a scene.
And also, a note on sorting: custom UI on the same plane as the video mesh will have undefined sorting behavior. Add a ModelSortGroupComponent to the same entity the VideoPlayerComponent is on, and use a ModelSortGroup that specifies a planarUIPlacement category, to explicitly sort the entity against co-planar UI. Whether its QuickLook, AVKit or RealityKit, it’s up to you to choose which framework to use for immersive video playback, and you have the tools to build these incredible experiences into your app!
Create new immersive experiences in your app by adding 360 APMP video to an immersive game, or streaming Apple Immersive Video or spatial video, among other use cases. To dig into the new APMP video types, watch the video “Learn about the Apple Projected Media Profile“ And to dive deeper into Apple Immersive Video, check out the video “Learn about Apple Immersive Video technologies.“ Now, go forth and create amazing immersive video experiences!