はじめに
SPZ形式の3Dモデルを表示するアプリを作りました
開発環境
- MacBook Air M1, 2020
- Cursor 2.1.50
- nvm 0.39.7
- Node.js 22.4.0
フォルダ構成
frontend: Vite + React + TypeScript のフロントエンド
my-spz-viewer/
├── README.md # このファイル
└── frontend/ # フロントエンドアプリケーション
├── index.html # HTMLエントリーポイント
├── package.json # 依存関係とスクリプト
├── vite.config.ts # Vite設定ファイル
├── tsconfig.json # TypeScript設定
├── tsconfig.node.json # Node.js用TypeScript設定
├── .eslintrc.cjs # ESLint設定
├── .gitignore # Git除外設定
└── src/ # ソースコード
├── main.tsx # Reactエントリーポイント
├── App.tsx # メインアプリケーションコンポーネント
├── App.css # アプリケーションスタイル
├── index.css # グローバルスタイル
└── components/ # Reactコンポーネント
└── SplatViewerR3F.tsx # SPZビューアコンポーネント(React Three Fiber統合)
導入
💬 splat形式のファイルをvite+react+tsで表示する方法
- 既存ライブラリ(gaussian-splats-3d / @sparkjsdev/spark / @zappar/three-gaussian-splat のどれか)を使ってサンプル動かしたい — 使いたいライブラリ名を指定して下さい。
- 「自前で PLY をパースして Three.js に反映」する最低限の parser を出してほしい(簡易版)。
💬 既存ライブラリ
A: @mkkellogg/gaussian-splats-3d を使った実際に動く最小構成プロジェクト(Vite の package.json、main.tsx、コンポーネント一式)をそのまま出します。
B: @sparkjsdev/spark を使った導入+ビルド手順つきの実例(WASM ビルド対応、SPZ を読み込む完全サンプル)を作ります。
💬 B: @sparkjsdev/spark を使った導入+ビルド手順つきの実例(WASM ビルド対応、SPZ を読み込む完全サンプル)を作ります。
いい選択です。`@sparkjsdev/spark`(以降「Spark」)は **SPZ / PLY / SPLAT 等のスプラット形式をブラウザ上で高速にレンダリング**するための Three.js 向けライブラリで、WASM(Rust)コンポーネントを持つため大きめデータにも強いです。以下は **Vite + React + TypeScript** プロジェクトに Spark を組み込み、WASM ビルドを含めて SPZ を読み込む「実行できる」手順とサンプルコードです。まず流れを示し、その後に具体的なコマンドとコードを載せます。主要なポイントはドキュメント・公式リポジトリに合わせてあります。([GitHub][1])
---
# 全体の流れ(要点)
1. Vite + React + TS のプロジェクトを作る。
2. 必要パッケージを入れる:`three`、`@react-three/fiber`(任意だが推奨)、`@sparkjsdev/spark`。([sparkjs.dev][2])
3. まず `npm install @sparkjsdev/spark` で済むか試す(公開パッケージに prebuilt な WASM が含まれていることが多い)。動かなければ(例:dev ブランチを使う/最新を手元でビルドしたい場合)Spark のソースをクローンして `npm run build`(内部で Rust → Wasm のビルドが走る)を実行。Rust が必要。([GitHub][1])
4. React 側では Three.js(あるいは react-three-fiber)と統合し、`SplatMesh`(高レベルなスプラットオブジェクト)や `SparkRenderer` をシーンに追加して `.spz` / `.ply` を読み込む。ドキュメントの `new SplatMesh({ url })` が最も簡単。([sparkjs.dev][2])
---
# 環境準備コマンド(例)
ターミナルで順に実行します。
1. Vite プロジェクト作成(TypeScript + React)
```bash
npm create vite@latest my-splat-app -- --template react-ts
cd my-splat-app
```
2. 依存インストール
```bash
npm install three @react-three/fiber @react-three/drei
npm install @sparkjsdev/spark
```
まずはこの状態で動かしてみてください。`@sparkjsdev/spark` が npm 経由でそのまま動くケースが多いです。([sparkjs.dev][2])
3. (必要なら)ローカルで Spark をビルドする — Rust が必要
* Rust を入れていない場合(推奨: rustup)
```bash
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# follow prompts, then restart shell
```
* Spark ソースをクローンしてビルド(WASM を含む)
```bash
git clone https://github.com/sparkjsdev/spark.git
cd spark
npm install
npm run build # README にあるビルド手順: wasm 部分は npm run build:wasm 等で個別実行可能
# ビルド後、ローカルパッケージをプロジェクトに追加する場合:
# from your app folder:
# npm add ../path/to/spark (or use npm link)
```
`npm run build` は内部で Rust → Wasm のビルドを呼びます(ローカルで dev 実行や最新ソースを利用したい場合に必要)。([GitHub][1])
---
# 実例コード(最小限で動く — React Three Fiber を使う)
以下は `@react-three/fiber` を使う例。`SplatMesh` を URL から読み込んでシーンに追加します。ファイル名は `src/components/SplatViewerR3F.tsx` としてください。
```tsx
// src/components/SplatViewerR3F.tsx
import React, { useEffect } from "react";
import { Canvas, useThree } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";
import * as THREE from "three";
// Spark の型は any にしておく(必要なら型定義を追加)
import { SplatMesh as SparkSplatMesh, SparkRenderer as SparkRendererClass } from "@sparkjsdev/spark";
function SparkScene({ url }: { url: string }) {
const { scene, gl, camera } = useThree();
useEffect(() => {
let splatObj: THREE.Object3D | null = null;
let sparkRenderer: any = null;
(async () => {
// SplatMesh を URL から作る(公式ドキュメントの使い方)
// @sparkjsdev/spark の export をそのまま使えるはず
const SplatMesh: any = (SparkSplatMesh as any) ?? (await import("@sparkjsdev/spark")).SplatMesh;
const SparkRenderer: any = (SparkRendererClass as any) ?? (await import("@sparkjsdev/spark")).SparkRenderer;
// SparkRenderer を scene に追加(任意。自動生成もされるが明示的に作る例)
sparkRenderer = new SparkRenderer({ renderer: gl }); // ドキュメント参照
scene.add(sparkRenderer);
// SplatMesh の作成(url オプションでロード)
splatObj = new SplatMesh({ url }); // URL はCORS対応済みであること
splatObj.position.set(0, 0, 0);
scene.add(splatObj);
})();
return () => {
if (splatObj) scene.remove(splatObj);
if (sparkRenderer) scene.remove(sparkRenderer);
};
}, [scene, gl, url]);
return null;
}
export default function SplatViewerR3F({ url }: { url: string }) {
return (
<div style={{ width: "100%", height: "100vh" }}>
<Canvas camera={{ position: [0, 0.5, 2], fov: 60 }}>
<ambientLight intensity={0.9} />
<SparkScene url={url} />
<OrbitControls />
</Canvas>
</div>
);
}
```
使い方(App.tsx など):
```tsx
import React from "react";
import SplatViewerR3F from "./components/SplatViewerR3F";
function App() {
return <SplatViewerR3F url="https://sparkjs.dev/assets/splats/butterfly.spz" />;
}
export default App;
```
上の `butterfly.spz` は Spark のドキュメントで使われているデモ資産の一例です(CORS 設定されている公開アセットを使うか、自ホストならサーバ側で CORS を許可してください)。([sparkjs.dev][2])
---
# トラブルシューティング / 実務上の注意
* **WASM が見つからない/ロード失敗**:`@sparkjsdev/spark` を npm で入れた場合は通常 prebuilt が含まれているが、開発版を取り込んだり local build したときは `npm run build`(Rust 必須)を実行して Wasm を生成する必要があります。エラーメッセージに `wasm` が含まれている場合はローカルビルドを試してください。([GitHub][1])
* **CORS**:外部 URL の SPZ を直接 `SplatMesh({ url })` に渡す場合、サーバが CORS を許可している必要があります。自分でホストするなら `Access-Control-Allow-Origin: *` 等を設定してください。
* **大きなファイル**:SPZ は圧縮フォーマットで小さいが、それでも数十〜数百MB のデータはブラウザ負荷が高いです。必要なら LOD・ストリーミング・オンデマンド読み込みを検討してください。([GitHub][3])
---
# 参考(公式ドキュメント・リポジトリ)
* Spark GitHub(README / ビルド手順). ([GitHub][1])
* Spark ドキュメント(Getting Started / SplatMesh) — `new SplatMesh({ url })` の使用例あり。([sparkjs.dev][2])
* React + r3f example(公式の `spark-react-r3f` サンプル) — r3f と組む場合の参考に。([GitHub][4])
---
必要なら次を即出します(どれか選んでください):
* 1. 上の手順どおりに動く **最小リポジトリの雛形**(`package.json`, `vite.config.ts`, `src/main.tsx`, `src/App.tsx`, `src/components/SplatViewerR3F.tsx` を丸ごと)をそのまま貼る。
* 2. `npm run build`(Rust/WASM)で出る典型的なエラーとその対策集(Rust のインストール/wasm-pack の使い方/PATH の設定など)。
* 3. あなたの手元の `.spz` ファイル(あるいは公開 URL)があるなら、それを読み込むための最適な CORS・ホスティングの設定例(nginx / static server)。
どれにしますか?選ばなければまず **1)** の最小リポジトリ雛形をそのまま出します。
[1]: https://github.com/sparkjsdev/spark?utm_source=chatgpt.com "An advanced 3D Gaussian Splatting renderer for THREE.js"
[2]: https://sparkjs.dev/docs/?utm_source=chatgpt.com "Getting Started"
[3]: https://github.com/nianticlabs/spz?utm_source=chatgpt.com "GitHub - nianticlabs/spz: File format for 3D Gaussian splats. ..."
[4]: https://github.com/sparkjsdev/spark-react-r3f?utm_source=chatgpt.com "sparkjsdev/spark-react-r3f"
💬 frontendにspzを表示するアプリを実装して
- package.jsonとvite設定ファイルを作成
- TypeScript設定ファイルを作成
- HTMLエントリーポイントとスタイルファイルを作成
- Reactアプリのメインコンポーネントを作成
- SPZビューアコンポーネントを作成
- その他の設定ファイル(.gitignore等)を作成
💬 下記のエラー文を貼り付け
three.module.js?v=0385234b:53034 WARNING: Multiple instances of Three.js being imported.
(anonymous) @ three.module.js?v=0385234b:53034Understand this warning
chunk-GX6MTTAZ.js?v=0385234b:48272 SPZファイルの読み込みに失敗しました: TypeError: THREE.Matrix2 is not a constructor
at _SparkRenderer.makeUniforms (spark.module.js?v=0385234b:10503:29)
at new _SparkRenderer (spark.module.js?v=0385234b:10375:37)
at loadSplat (SplatViewerR3F.tsx:43:31)
console.error @ chunk-GX6MTTAZ.js?v=0385234b:48272Understand this error
spark.module.js?v=0385234b:598 `WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:
TypeError: Failed to execute 'compile' on 'WebAssembly': HTTP status code is not ok
__wbg_load @ spark.module.js?v=0385234b:598Understand this warning
spark.module.js?v=0385234b:605 Uncaught (in promise) CompileError: WebAssembly.instantiate(): BufferSource argument is empty
at __wbg_load (spark.module.js?v=0385234b:605:30)
at async __wbg_init (spark.module.js?v=0385234b:712:32)
at async _SplatMesh.staticInitialize (spark.module.js?v=0385234b:7262:5)
- vite.config.ts - WASMファイルの処理を追加
- package.json - Three.jsのバージョンを調整
- src/main.tsx - Three.jsのグローバル設定
- src/components/SplatViewerR3F.tsx - エラーハンドリング改善
- index.html - 不要な参照を削除
💬 下記のエラー文を貼り付け
SplatViewerR3F.tsx:75 SPZファイルの読み込みに失敗しました: TypeError: THREE.Matrix2 is not a constructor
at _SparkRenderer.makeUniforms (spark.module.js?v=51124b03:10503:29)
at new _SparkRenderer (spark.module.js?v=51124b03:10375:37)
at loadSplat (SplatViewerR3F.tsx:61:31)
console.error @ chunk-FPLVK4TI.js?v=51124b03:17705
loadSplat @ SplatViewerR3F.tsx:75
await in loadSplat
(anonymous) @ SplatViewerR3F.tsx:80
commitHookEffectListMount @ chunk-FPLVK4TI.js?v=51124b03:10911
commitPassiveMountOnFiber @ chunk-FPLVK4TI.js?v=51124b03:12219
commitPassiveMountEffects_complete @ chunk-FPLVK4TI.js?v=51124b03:12191
commitPassiveMountEffects_begin @ chunk-FPLVK4TI.js?v=51124b03:12181
commitPassiveMountEffects @ chunk-FPLVK4TI.js?v=51124b03:12171
flushPassiveEffectsImpl @ chunk-FPLVK4TI.js?v=51124b03:13939
flushPassiveEffects @ chunk-FPLVK4TI.js?v=51124b03:13902
performSyncWorkOnRoot @ chunk-FPLVK4TI.js?v=51124b03:13305
flushSyncCallbacks @ chunk-FPLVK4TI.js?v=51124b03:2766
commitRootImpl @ chunk-FPLVK4TI.js?v=51124b03:13887
commitRoot @ chunk-FPLVK4TI.js?v=51124b03:13743
finishConcurrentRender @ chunk-FPLVK4TI.js?v=51124b03:13242
performConcurrentWorkOnRoot @ chunk-FPLVK4TI.js?v=51124b03:13155
workLoop @ chunk-FPLVK4TI.js?v=51124b03:274
flushWork @ chunk-FPLVK4TI.js?v=51124b03:253
performWorkUntilDeadline @ chunk-FPLVK4TI.js?v=51124b03:461Understand this error
SplatViewerR3F.tsx:76 エラー詳細: TypeError: THREE.Matrix2 is not a constructor
at _SparkRenderer.makeUniforms (http://localhost:3000/node_modules/@sparkjsdev/spark/dist/spark.module.js?v=51124b03:10503:29)
at new _SparkRenderer (http://localhost:3000/node_modules/@sparkjsdev/spark/dist/spark.module.js?v=51124b03:10375:37)
at loadSplat (http://localhost:3000/src/components/SplatViewerR3F.tsx:54:31)
console.error @ chunk-FPLVK4TI.js?v=51124b03:17705
loadSplat @ SplatViewerR3F.tsx:76
await in loadSplat
(anonymous) @ SplatViewerR3F.tsx:80
commitHookEffectListMount @ chunk-FPLVK4TI.js?v=51124b03:10911
commitPassiveMountOnFiber @ chunk-FPLVK4TI.js?v=51124b03:12219
commitPassiveMountEffects_complete @ chunk-FPLVK4TI.js?v=51124b03:12191
commitPassiveMountEffects_begin @ chunk-FPLVK4TI.js?v=51124b03:12181
commitPassiveMountEffects @ chunk-FPLVK4TI.js?v=51124b03:12171
flushPassiveEffectsImpl @ chunk-FPLVK4TI.js?v=51124b03:13939
flushPassiveEffects @ chunk-FPLVK4TI.js?v=51124b03:13902
performSyncWorkOnRoot @ chunk-FPLVK4TI.js?v=51124b03:13305
flushSyncCallbacks @ chunk-FPLVK4TI.js?v=51124b03:2766
commitRootImpl @ chunk-FPLVK4TI.js?v=51124b03:13887
commitRoot @ chunk-FPLVK4TI.js?v=51124b03:13743
finishConcurrentRender @ chunk-FPLVK4TI.js?v=51124b03:13242
performConcurrentWorkOnRoot @ chunk-FPLVK4TI.js?v=51124b03:13155
workLoop @ chunk-FPLVK4TI.js?v=51124b03:274
flushWork @ chunk-FPLVK4TI.js?v=51124b03:253
performWorkUntilDeadline @ chunk-FPLVK4TI.js?v=51124b03:461Understand this error
spark.module.js?v=51124b03:598 `WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:
TypeError: Failed to execute 'compile' on 'WebAssembly': HTTP status code is not ok
__wbg_load @ spark.module.js?v=51124b03:598
await in __wbg_load
__wbg_init @ spark.module.js?v=51124b03:712
await in __wbg_init
staticInitialize @ spark.module.js?v=51124b03:7262
(anonymous) @ spark.module.js?v=51124b03:7619Understand this warning
spark.module.js?v=51124b03:605 Uncaught (in promise) CompileError: WebAssembly.instantiate(): BufferSource argument is empty
at __wbg_load (spark.module.js?v=51124b03:605:30)
at async __wbg_init (spark.module.js?v=51124b03:712:32)
at async _SplatMesh.staticInitialize (spark.module.js?v=51124b03:7262:5)
- vite.config.ts - CORSヘッダーを削除、Three.jsのエイリアスを追加
- package.json - Three.jsのバージョンを調整
- src/components/SplatViewerR3F.tsx - デバッグ情報とエラーハンドリングを改善
💬 web検索して確認して
- @sparkjsdev/sparkの公式リポジトリを確認
- Three.jsのバージョンを試行
💬 最新版にしてみよう
更新内容
- three: ^0.156.0 → ^0.175.0(最新版)
- @types/three: ^0.156.0 → ^0.175.0(型定義も更新)
- @sparkjsdev/spark: ^0.1.0 → ^0.1.7(最新版)
💬 下記のエラー文を貼り付け
spark.module.js?v=1fb0046d:598 `WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:
TypeError: Failed to execute 'compile' on 'WebAssembly': HTTP status code is not ok
__wbg_load @ spark.module.js?v=1fb0046d:598
await in __wbg_load
__wbg_init @ spark.module.js?v=1fb0046d:712
await in __wbg_init
staticInitialize @ spark.module.js?v=1fb0046d:7262
(anonymous) @ spark.module.js?v=1fb0046d:7619Understand this warning
spark.module.js?v=1fb0046d:605 Uncaught (in promise) CompileError: WebAssembly.instantiate(): BufferSource argument is empty
at __wbg_load (spark.module.js?v=1fb0046d:605:30)
at async __wbg_init (spark.module.js?v=1fb0046d:712:32)
at async _SplatMesh.staticInitialize (spark.module.js?v=1fb0046d:7262:5)
- server.headersにCORSヘッダーを追加(WASM読み込みに必要)
- worker設定を追加(WASMがワーカーで使用される場合)
💬 できました。ファイルからも読み込めるようにして下さい。
- App.tsx
- App.css
- SplatViewerR3F.tsx
実行
実行してみましょう
cd frontend
npm install
npm run dev
http://localhost:5173 にアクセス
spzビューワー作った pic.twitter.com/8g96VsHgjD
— がちもとさん (@sotongshi) December 10, 2025
お疲れ様でした。
リポジトリはこちら