Display capability setup, display-capable device selection, UI DSL, icons, buttons, images, and video playback
npx skills add https://github.com/facebook/meta-wearables-dat-ios --skill display-access
Use MWDATDisplay to render content on Meta Ray-Ban Display glasses.
Use this skill with getting-started and permissions-registration when creating a full app. A Display app still needs Wearables.configure() at launch, Info.plist URL scheme configuration, URL callbacks through Wearables.shared.handleUrl(_:), and completed Meta AI registration before it can create a session.
Add MWDATDisplay to the same app target that already uses MWDATCore. Import it next to core:
import MWDATCore
import MWDATDisplay
Use the getting-started Info.plist setup, then mirror the DisplayAccess sample for Display sessions:
CFBundleURLTypes URL scheme and route callbacks to Wearables.shared.handleUrl(_:).MWDAT, set AppLinkURLScheme, MetaAppID, ClientToken, and TeamID. MetaAppID = 0 is for Developer Mode; production apps should use Wearables Developer Center values for MetaAppID and ClientToken, plus the Apple Developer Team ID.UISupportedExternalAccessoryProtocols with com.meta.ar.wearable, UIBackgroundModes entries for external-accessory and bluetooth-central, and NSBluetoothAlwaysUsageDescription. The DisplayAccess sample also includes bluetooth-peripheral and processing.NSLocalNetworkUsageDescription and NSBonjourServices. Display acquires link leases: core device discovery/session setup uses medium then low links, while Display uses medium then high links when available.Use the public display filter when creating an automatic selector:
let wearables = Wearables.shared
let deviceSelector = AutoDeviceSelector(
wearables: wearables,
filter: { device in device.supportsDisplay() }
)
Use SpecificDeviceSelector(device: selectedDevice.identifier) instead when your UI lets the user pick a specific Device. The initializer takes a DeviceIdentifier, not the whole Device.
AutoDeviceSelector updates from devicesStream(). Create it before the user taps the Display action, or wait for activeDeviceStream() to yield a non-nil device before calling createSession(deviceSelector:); otherwise createSession can throw DeviceSessionError.noEligibleDevice.
For a picker or Settings screen, list devices from Wearables.shared.devicesStream(), then look up metadata from deviceForIdentifier(_:). The DisplayAccess sample shows nameOrId(), deviceType().rawValue, linkState, and compatibility() and keeps addLinkStateListener / addCompatibilityListener tokens alive while rows are visible. If compatibility() == .deviceUpdateRequired, surface a firmware update action through Wearables.shared.openFirmwareUpdate().
Create and start the DeviceSession, wait for .started, then add and start Display. Keep the state listener token alive for as long as you need updates, and observe session.errorStream() for async session failures.
import MWDATCore
import MWDATDisplay
@MainActor
final class DisplayController {
private var deviceSession: DeviceSession?
private var display: Display?
private var displayStateToken: AnyListenerToken?
private var sessionErrorTask: Task<Void, Never>?
func connect() async {
do {
let wearables = Wearables.shared
let selector = AutoDeviceSelector(
wearables: wearables,
filter: { $0.supportsDisplay() }
)
let session = try wearables.createSession(deviceSelector: selector)
deviceSession = session
sessionErrorTask = Task { [weak self] in
for await error in session.errorStream() {
await self?.showError(error.localizedDescription)
}
}
let sessionStarted = Task {
for await state in session.stateStream() {
if state == .started {
return
}
}
}
do {
try session.start()
await sessionStarted.value
} catch {
sessionStarted.cancel()
throw error
}
let capability = try session.addDisplay()
display = capability
displayStateToken = capability.statePublisher.listen { [weak self] state in
Task { @MainActor in
if state == .started {
self?.showStatusCard()
}
}
}
capability.start()
} catch DeviceSessionError.datAppOnTheGlassesUpdateRequired {
showDATGlassesAppUpdate()
} catch {
showError(error.localizedDescription)
}
}
func disconnect() async {
display?.onPlaybackEvent = nil
display?.stop()
deviceSession?.stop()
sessionErrorTask?.cancel()
sessionErrorTask = nil
displayStateToken = nil
display = nil
deviceSession = nil
}
}
For user-triggered content, match the sample's pending-action pattern: if the user taps "Try it" before Display is connected, store the send as a pending async action, attach to Display, and run the action when DisplayState.started arrives. Reset the display session when registration changes to .available or .unavailable.
Build exactly one root DisplayableView per send(_:) call. Each send replaces the previous content on the glasses and replaces the active tap handlers. Use a root FlexBox for UI, or a root VideoPlayer for video. Do not send Text, Button, Image, or Icon as the root; place them inside a FlexBox.
If a file also imports SwiftUI, Display DSL names such as Text, Button, and Image can be ambiguous. Prefer keeping Display builders in files that import MWDATDisplay without SwiftUI, or qualify the symbols as MWDATDisplay.Text, MWDATDisplay.Button, and MWDATDisplay.Image.
func showStatusCard() {
Task {
do {
try await display?.send(
FlexBox(direction: .column, spacing: 12) {
Text("Bike ride", style: .heading)
Text("Turn right in 200 ft", style: .body, color: .secondary)
Button(
label: "Done",
style: .primary,
iconName: .checkmark,
onClick: { print("Done tapped") }
)
}
.padding(24)
.background(.card)
.onTap { print("Card tapped") }
)
} catch {
showError((error as? DisplayError)?.description ?? error.localizedDescription)
}
}
}
Use HTTPS image URLs for image content, and use the IconName enum for built-in icons. Do not invent raw icon strings.
try await display.send(
FlexBox(direction: .row, spacing: 8, crossAlignment: .center) {
Image(
uri: "https://example.com/thumbnail.png",
sizePreset: .fill,
cornerRadius: .medium
)
Icon(name: .gear, style: .filled)
Text("Device settings", style: .body)
}
.padding(24)
)
For URL-based video, send a root VideoPlayer. Use VideoPlayer(onError:) for video-specific errors, and use display.onPlaybackEvent for playback events. Set onPlaybackEvent before sending the video, clear it after terminal events if the flow is complete, and call sendVideoStop() if the user exits playback early. Blank or non-HTTP(S) URLs throw DisplayError.invalidVideoURL.
display.onPlaybackEvent = { event in
if event.type == .ended || event.type == .stopped {
Task { @MainActor in
display.onPlaybackEvent = nil
showStatusCard()
}
}
}
try await display.send(
VideoPlayer(
provider: .uri("https://example.com/tutorial.mp4"),
codec: .mp4,
onError: { error in
Task { @MainActor in
showError(error.localizedDescription)
}
}
)
)
Wearables.configure() at app launch and complete registration before creating the session.DeviceSession to reach .started before calling addDisplay().DeviceSessionError.datAppOnTheGlassesUpdateRequired separately and offer Wearables.shared.openDATGlassesAppUpdate().display.start(), then wait for DisplayState.started through statePublisher before sending user-triggered content.session.errorStream() so async session failures are surfaced.Wearables.shared.handleUrl(_:).nameOrId(), deviceType(), linkState, and compatibility() for device rows; keep link/compatibility listener tokens until the row is gone.FlexBox.onTap and Button(label:onClick:) for interactions. The callbacks belong to the most recent sent view.FlexBox, Text, Button, Image, Icon, VideoPlayer, IconName, TextStyle, TextColor, ButtonStyle, ImageSize, CornerRadius, Direction, Alignment, Background, Edge, and EdgeInsets.display.onPlaybackEvent when the video flow is finished if you no longer need playback callbacks.DeviceSession when the display experience ends.Use the Display Access sample app for a complete flow: registration, device selection, display attachment, interactive content, and video.
Create beautiful visual art in .png and .pdf documents using design philosophy. You should use this skill when the user asks to create a poster, piece of art, design, or other static piece. Create original visual designs, never copying existing artists' work to avoid copyright violations.
Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.
Improves the quality of images, especially screenshots, by enhancing resolution, sharpness, and clarity. Perfect for preparing images for presentations, documentation, or social media posts.
Downloads videos from YouTube and other platforms for offline viewing, editing, or archival. Handles various formats and quality options.
Lightweight WSI tile extraction and preprocessing. Use for basic slide processing tissue detection, tile extraction, stain normalization for H&E images. Best for simple pipelines, dataset preparation, quick tile-based analysis. For advanced spatial proteomics, multiplexed imaging, or deep learning pipelines use pathml.
Microscopy data management platform. Access images via Python, retrieve datasets, analyze pixels, manage ROIs/annotations, batch processing, for high-content screening and microscopy workflows.
Python library for working with DICOM (Digital Imaging and Communications in Medicine) files. Use this skill when reading, writing, or modifying medical imaging data in DICOM format, extracting pixel data from medical images (CT, MRI, X-ray, ultrasound), anonymizing DICOM files, working with DICOM metadata and tags, converting DICOM images to other formats, handling compressed DICOM data, or processing medical imaging datasets. Applies to tasks involving medical image analysis, PACS systems, radiology workflows, and healthcare imaging applications.
This skill should be used when working with pre-trained transformer models for natural language processing, computer vision, audio, or multimodal tasks. Use for text generation, classification, question answering, translation, summarization, image classification, object detection, speech recognition, and fine-tuning models on custom datasets.
Take facebook/display-access from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.