Highlight
visionOS 26 の Safari では HTML
<model>要素がデフォルトで有効化され、Web ページ内に USDZ ファイルを立体的にインライン表示できるようになりました。さらに Website Environments により、サイト側から没入背景を指定することも可能になっています。
主要内容
これまで十数年にわたり、Web 上で 3D モデルを表示する手段は Three.js や Babylon.js などの JavaScript ライブラリに頼り、2D の canvas 上にジオメトリを描画するというものでした。ユーザーが目にしているのはあくまで平面画像で、奥行きはなく、回り込んで別の角度から眺めることもできません。視覚的には 3D でも、体験としては 2D に留まっていたのです。
visionOS 26 の Safari では、長年議論されてきた HTML <model> 要素が導入されました。文法は <img> と同じくらいシンプルですが、レンダリングの挙動はまったく異なります。ページ表面に「窓」を開け、その奥の仮想空間に USDZ モデルを立体的に配置するイメージです。ユーザーは頭を動かして別の角度から観察したり、ピンチしてモデルを掴み Quick Look に引き出して実寸サイズで確認したり、さらには別の app へドラッグすることもできます。この要素はドキュメントフロー、CSS、JavaScript と完全に統合されています。background-color で仮想空間の背景色をページの背景と揃えたり、backdrop-filter でモデルの上にすりガラス調のパネルを重ねたり、position: absolute で他の DOM 要素とモデルを重ね合わせたりできます。
<model> 要素のほかに、本 session では没入メディア(180/360 度動画、Apple Immersive Video、空間写真)を <video> / <img> でインライン再生・全画面表示する方法、そして developer preview の <link rel="spatial-backdrop"> によってサイト独自の USDZ 環境を指定し、訪問者が選択して没入したうえで閲覧できる仕組みも紹介されています。
詳細
<model> の最小構成(01:00)
<model src="teapot.usdz"></model>
ポイント:
srcには USDZ ファイルを指定し、<model>要素は明示的に閉じる必要があります(<img>のような void 要素ではありません)。- ブラウザは bounding box の x-y 面を中心に要素内へスケーリングします。モデルは常にページ表面より奥にレンダリングされ、スクロールでビューポートからはみ出ても「飛び出す」ことはありません。
サーバーには正しい MIME type が必要(05:30)
# NGINX mime.types
types {
...
model/vnd.usdz+zip usdz;
}
ポイント:
- 古い CDN やサーバーは
.usdzをapplication/octet-streamとして配信してしまうことがあり、その場合ブラウザはレンダリングを拒否します。 - Apache では
AddType model/vnd.usdz+zip .usdzを、Python 標準のhttp.serverではHandler.extensions_mapの修正が必要です。
フォールバックを用意する(05:51 ~ 06:17)
<!-- <model-viewer> library from https://modelviewer.dev/ -->
<script type="module"
src="https://ajax.googleapis.com/ajax/libs/model-viewer/4.0.0/model-viewer.min.js">
</script>
<model src="camera.usdz">
<!-- Fallback experience for backward compatibility -->
<model-viewer src="camera.glb"></model-viewer>
</model>
ポイント:
- フォールバックの内容は
<model>の内側に直接書きます。<model>をサポートするブラウザはこれを無視し、未対応のブラウザは内部のコンテンツをレンダリングします。 - 最もシンプルなフォールバックは
<img>1 枚です。回転体験を維持したいなら<model-viewer>のような 2D ライブラリを埋め込みます。
user agent ではなく機能で検出する(06:52)
if (window.HTMLModelElement) {
// Supported by this browser
} else {
// Not supported by this browser
}
ポイント:
- グローバルオブジェクト
window.HTMLModelElementの有無で判定します。 - user agent 文字列での判定は避けてください。将来的に他のブラウザがこの仕様を実装したときに誤判定の原因となります。
ローディングインジケーター(07:32)
<model src="camera.usdz" id="mymodel"></model>
<script>
const mymodel = document.getElementById("mymodel");
if (window.HTMLModelElement) {
mymodel.ready.then(result => {
// Hide the loading indicator
// Show the model
}).catch(error => {
// Loading error occurred, show a retry button
});
}
</script>
ポイント:
model.readyは Promise を返し、リソースの読み込みが完了すると resolve します。- USDZ ファイルは 10MB を超えることも珍しくなく、低速回線では数秒かかります。ローディング表示を出すことで、ユーザーがそのままページを閉じてしまうのを防げます。
.catchではリトライボタンを用意して、ネットワーク失敗に備えましょう。
IBL によるライティング制御(10:56)
<model src="camera.usdz" environmentmap="sunset.exr"></model>
ポイント:
environmentmap属性で image-based lighting 用の画像(正距円筒図法)を指定します。- フォーマットは OpenEXR または Radiance HDR を推奨します。複数オーダーの輝度を表現できるため反射が自然になります。JPEG ではモデルが平坦に見えてしまいます。
- IBL ファイルは USDZ にバンドルしないので、シーンごとに異なるライティングを適用しやすくなっています。
JavaScript で姿勢を制御する(13:49)
<model src="teapot.usdz" id="mymodel"></model>
<a onclick="turnRight()">Right</a>
<script>
const mymodel = document.getElementById("mymodel");
function turnRight() {
const matrix = mymodel.entityTransform; // DOMMatrixReadOnly
const newMatrix = matrix.rotateAxisAngle(0, 1, 0, 90);
mymodel.entityTransform = newMatrix;
}
</script>
ポイント:
entityTransformはDOMMatrixReadOnlyで、現在のモデルの姿勢を取得できます。rotateAxisAngle(0, 1, 0, 90)で y 軸まわりに 90 度回転させ、結果をentityTransformに代入し直すことでレンダリングが更新されます。- 初期状態に戻したい場合は
model.entityTransform = new DOMMatrix();を使います。
タイムライン上の任意位置にジャンプする(17:35)
<model src="camera.usdz" id="mymodel"></model>
<script>
const mymodel = document.getElementById("mymodel");
function openFlash() {
mymodel.currentTime = 1; // Unit is seconds
}
function openScreen() {
mymodel.currentTime = 3; // Unit is seconds
}
</script>
ポイント:
currentTimeの単位は秒で、USDZ ファイル内のアニメーションタイムラインに対応します。<input type="range">と組み合わせれば、手動で動かせるアニメーションスクラバーを作れます。- 自動再生したい場合は
<model loop autoplay>を、JavaScript で制御する場合はmodel.play()/model.pause()を使い、状態はmodel.pausedで取得します。
Three.js で USDZ を動的生成する(19:35)
import * as THREE from "three";
import { USDZExporter } from "three/examples/exporters/USDZExporter.js";
async function generateModel() {
const scene = new THREE.Scene();
// ... create a really nice scene procedurally ...
const bytes = await new USDZExporter().parseAsync(scene);
const objURL = URL.createObjectURL(new Blob([bytes]));
const mymodel = document.getElementById("mymodel");
mymodel.setAttribute("src", objURL);
}
ポイント:
- Three.js には
USDZExporterが同梱されており、プロシージャルに生成したシーンを USDZ のバイト列として書き出せます。 URL.createObjectURLで blob URL に包み、<model>のsrcに渡すだけでブラウザが立体的にレンダリングします。- パラメトリックな製品コンフィギュレーターに最適です。色やサイズの変更 → USDZ を再生成 → そのままプレビュー、という流れが組めます。
没入メディアのインライン再生(23:10)
<video src="spatial_video.mov"></video> <!-- Single file -->
<video src="360_video.m3u8"></video> <!-- HTTP Live Streaming -->
ポイント:
- 既存の
<video>タグが 180/360 度動画、広角動画、Apple Immersive Video にそのまま対応します。 - ページ内ではデフォルトで 2D インライン再生され、
player.requestFullScreen()を呼び出すと正しい投影方式でユーザーを包み込む形で全画面表示されます。 - 空間写真は
<img src="spatial.heic" controls>だけで OK です。controls属性を付与すると、没入ビューを開くボタンが表示されます。
Website Environments(developer preview、26:49)
<link rel="spatial-backdrop" href="office.usdz" environmentmap="lighting.hdr">
ポイント:
<link>でページ全体に対する USDZ の没入背景を宣言します。- ユーザーが能動的に「没入」を選択すると、部屋全体が office.usdz の環境に置き換わります。
- 現状は developer preview の段階で、Safari の設定から手動で有効化する必要があります。
重要ポイント
-
やること:プロダクトの 3D 表示を 2D canvas から
<model>へ移行する- 価値:EC、家具、カメラ、玩具などの商品は visionOS の Safari 上で立体的に表示できるようになります。頭を動かして角度を変えながら確認できる体験は、従来の「360 度画像集」とは没入感が一段違ううえ、native app を必要としません。
- 始め方:iPhone と Reality Composer でサンプルを USDZ 化 → Mac の Preview で圧縮テクスチャ版を書き出して 5MB 以内に収める → 既存の商品ページに
<model src>を追加し、<img>でフォールバックを用意する。
-
やること:
<model>と DOM でプロダクトコンフィギュレーターを作る- 価値:WebGL のレンダリングパイプラインを自前で構築する必要はありません。CSS だけで frosted glass パネル、テキストレイヤー、ボタンを実装でき、構成切り替えは
srcを差し替えるか Three.js でその場で USDZ を生成するだけで済みます。 - 始め方:まず既存ページで Three.js を使ったプロシージャルなシーン生成を動かす →
USDZExporterを組み込んで blob URL を出力 →model.readyでローディング状態を制御する。
- 価値:WebGL のレンダリングパイプラインを自前で構築する必要はありません。CSS だけで frosted glass パネル、テキストレイヤー、ボタンを実装でき、構成切り替えは
-
やること:ブランドの没入ショートフィルムを公式サイトに掲載する
- 価値:Apple Immersive Video は visionOS の Safari 上で
<video>のインライン再生と全画面没入が可能で、App Store への申請を省略できます。一般の訪問者には 2D インライン再生で表示され、Vision Pro ユーザーは全画面化することで 180 度の没入体験になります。 - 始め方:素材を HLS(
.m3u8)に変換 → 1 つの<video>タグで両方のクライアントに配信 → デスクトップ向けにはポスター画像でフォールバックを用意する。
- 価値:Apple Immersive Video は visionOS の Safari 上で
-
やること:
spatial-backdropを試してブランドサイトに専用環境を作る- 価値:developer preview の早期検証として、訪問者を「店舗の中に没入させる」体験を提供できます。先行事例となれば、メディアからの注目も期待できます。
- 始め方:Reality Composer Pro で店舗やショールームの簡単な USDZ を作成 → HDR ライティングを 1 枚用意 →
<link rel="spatial-backdrop">を追加し、Safari の実験的機能を有効化して動作確認する。
関連セッション
- Optimize your 3D assets for spatial computing — WWDC24 で USDZ ファイルの圧縮、テクスチャとジオメトリの制御を扱う実践ガイド
- Meet Reality Composer Pro — Mac 上で USDZ モデルとシーンを構築する入門 session
- Design hover interactions for visionOS — visionOS 向けの hover フィードバックと、model 要素の視線インタラクションの設計
- Design widgets for visionOS — visionOS 26 上で空間環境に溶け込む widget の設計
コメント
GitHub Issues · utterances