Claude CodeでBlenderを動かしWebに載せる|glb書き出しとthree.js再生


Claude CodeでBlenderを動かしてWebに載せるには、動きをPythonスクリプトで組ませてBlenderを画面なしで実行し、glbで書き出したファイルをthree.jsのGLTFLoaderで読み込んでAnimationMixerで再生します。Blenderで決めた動きが、タイミングもイージングもそのままブラウザで再生されます。

ClaudeとBlenderの連携というと、MCPで3Dのシーンを作らせてみた、という話が目立ちます。Web制作で困るのはその先です。書き出したglbに動きが入っていない、向きが90度ずれる、圧縮したら読み込めない、再生が終わると止まったまま動かない。どれも、作ってからページに載せるまでの間で起こります。

この記事では、筆者が個人サイトORECTICのファーストビューで、Blenderで作った渾天儀の組み上がる動きをthree.jsで再生するまでに使った手順を紹介します。仕様は2026年10月20日に公式ドキュメントで確認しました。手元の環境はBlender 5.2.2 LTSとthree.js 0.170で、記事のサンプルコードはこの環境で実際に動かしています。


ClaudeからBlenderを動かす3つの方法

ClaudeからBlenderを動かす方法は、公式のBlenderコネクタ、コミュニティ製のMCP for Blender、Blenderを画面なしで起動してスクリプトを実行する方法の3つです。前の2つはMCPで、開いているBlenderをClaudeが会話しながら操作します。3つ目はMCPを使わず、Claude Codeが書いたPythonをBlenderのコマンドで実行します。

公式Blenderコネクタ(Blender Lab)MCP for Blender(旧blender-mcp)画面なしスクリプト
作っているところBlenderの開発者(Blender Lab)個人開発者(Blender財団とは無関係)自分(Claude Codeが書く)
Blenderの要件5.1以上3.0以上制限なし(使うAPIによる)
Claudeとのつなぎ方Claude Desktopのコネクタ/MCPクライアントMCPクライアント(uvxで起動)Claude CodeがBashで実行
操作のしかた開いているBlenderを会話で操作開いているBlenderを会話で操作毎回起動して、終わったら閉じる
同じ結果を作り直せるか会話の記録次第会話の記録次第スクリプトを再実行すれば同じになる
向く用途試作・シーンの把握・動きを探る試作・シーンの把握・動きを探る本番に載せる素材を作る

公式のコネクタは、Anthropicが2026年4月28日にClaude for Creative Workで発表しました。発表には「The Blender developers have created an MCP connector, which is now officially available for Claude」とあります。中身はBlender Labが公開しているBlender MCPで、v1.0.0が2026年4月27日、v1.0.3が9月11日に出ています。

MCP for Blenderは、コネクタが出る前から使われてきたコミュニティ製のMCPサーバーです。READMEには「This is a third-party integration and not made by Blender」と書かれています。ライセンスはMITで、PyPIの最新版は2026年10月20日公開の2.2.1です。更新が多いので、使う前にREADMEで最新の設定方法を確認してください。

Claude CodeにMCPを追加するコマンド

Claude CodeからMCP for Blenderを使う場合は、Blender側にアドオンを入れて接続を開始してから、次のコマンドでサーバーを追加します。Claude Codeでは、起動コマンドを--の後ろに書くのが公式の書式です。

claude mcp add blender -- uvx mcp-for-blender

公式のコネクタはClaude DesktopのCustomize → Connectorsから追加します。Anthropicのチュートリアルによると、コネクタはBlenderと同じマシンで動く必要があるためClaude Desktopが必要で、Claude Codeなど他のMCPクライアントからも使えます。


Web制作の本番は画面なしスクリプトで作る理由

ページに載せる素材は、画面なしスクリプトで作るのが確実です。MCPは「この輪をもう少し早く寄せて」と会話しながら探るのに向いています。ただ、決まった動きを色違い・サイズ違いで作り直す、書き出しの設定を毎回同じにする、という本番の作業には、スクリプトに残す方式が合っています。

  • 作り直せる:シーンも動きもコードなので、配色やフレーム数の定数を変えて再実行すれば同じ構成で出し直せる
  • 書き出し設定が固定される:上方向の変換・Draco圧縮・アニメーションの書き出し方をスクリプトに書いておけば、書き出すたびに設定が変わることがない
  • 実行するコードを先に読める:Claude Codeが書いたスクリプトを確認してから実行できる。MCPはAIが書いたコードをその場でBlenderに渡す
  • 失敗を検知できる:--python-exit-codeを付けると、スクリプトのエラーをコマンドの終了コードで受け取れる

筆者は、動きの方向を決めるまでは静止画を何枚も描かせて見比べ、決まったら同じスクリプトでglbを書き出す、という流れにしています。公式のBlender MCPにも、画面なしで起動したBlenderでPythonを実行するツール(execute_blender_code_for_cli)が入っているので、MCPから同じ方式を呼ぶこともできます。


Claude Codeに作らせる手順

手順は、シーンをPythonで組む、主要なフレームだけ描いて確かめる、動きの案を比べて決める、glbで書き出す、three.jsで再生する、の5段階です。Claude Codeには最初に「何を・どのくらいの長さで・どこに載せるか」を渡します。

  1. シーンをPythonで組む:形・マテリアル・キーフレームをすべてスクリプトに書かせる。動きの数値(フレーム数・イージング)は定数にまとめさせる
  2. 主要なフレームだけ低解像度で描く:blender -b -P scene.py -- --frames 1,40,90のように数枚だけ描き、1枚に並べて形と構図を確認する
  3. 動きの案を2〜3案作って比べる:静止画では動きの良し悪しが分からないので、低解像度の動画で並べて見て決める
  4. glbで書き出す:決まったスクリプトに書き出しの処理を足し、本番用のファイルを作る
  5. three.jsで再生する:GLTFLoaderで読み込み、AnimationMixerで再生する。ページの表示と合わせて最終確認する

Claude Codeへの頼み方

筆者は次のような条件を最初に渡しています。ポイントは、書き出し先と、Blenderの外で付ける要素(文字・光・回り続ける動き)を先に分けておくことです。

Blender 5.2をスクリプトで動かして、Webのファーストビューに載せる3Dの動きを作って。
- シーンは scene.py にすべてコードで組む(手作業の操作はしない)
- 30fps・96フレームで、部品が外側から集まって組み上がる。行き過ぎて揺れる動きは付けない
- 実行は blender -b -P scene.py -- <引数>。--frames で静止画、--glb で3Dデータを書き出す
- JSで差し替える部品と回す部品には名前を付ける(Core、Ring1〜3)
- 文字・ナビ・光の効果はBlenderに入れない(HTMLとthree.js側で付ける)
- 書き出しは +Y上・Draco圧縮・カメラとライトは含めない

サンプル:動く立方体をglbで書き出す

最小の例です。立方体を下から上げながら1回転させ、glbで書き出します。Blender 5.2.2で実行し、アニメーションが入ったglbが出力されることを確認しました。

import sys
import bpy

# 「--」より後ろの引数だけを受け取る
argv = sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else []
out = argv[0] if argv else "logo.glb"

# 空のシーンから始める
bpy.ops.wm.read_factory_settings(use_empty=True)
scene = bpy.context.scene
scene.render.fps = 30
scene.frame_start, scene.frame_end = 1, 60

# 立方体を置き、下から上がりながら1回転させる
bpy.ops.mesh.primitive_cube_add(size=1)
cube = bpy.context.active_object
cube.name = "Logo"

cube.location = (0, 0, -2)
cube.rotation_euler = (0, 0, 0)
cube.keyframe_insert("location", frame=1)
cube.keyframe_insert("rotation_euler", frame=1)

cube.location = (0, 0, 0)
cube.rotation_euler = (0, 0, 6.2832)
cube.keyframe_insert("location", frame=60)
cube.keyframe_insert("rotation_euler", frame=60)

# glbで書き出す(+Y上・Draco圧縮・シーン全体の動きを焼き込む)
bpy.ops.export_scene.gltf(
    filepath=out,
    export_format="GLB",
    export_yup=True,
    export_animation_mode="SCENE",
    export_force_sampling=True,
    export_cameras=False,
    export_lights=False,
    export_draco_mesh_compression_enable=True,
)
print("exported:", out)

実行は次のコマンドです。--より後ろはBlenderが読まずにスクリプトへ渡すので、出力先などの引数はそこに書きます。

blender -b -P scene.py -- logo.glb

Blenderのコマンドライン引数のマニュアルによると、-b(--background)は画面なしで起動するオプションで、引数は書いた順に実行されます。-bを-Pより前に書くのはこのためです。

サンプル:three.jsで再生する

書き出したglbを読み込んで再生するコードです。three.jsはimport mapで読み込み、Draco圧縮したglbを読むためにDRACOLoaderを渡しています。three.js 0.170で、クリップが読み込まれて再生されることを確認しました。

import * as THREE from "three";
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
import { DRACOLoader } from "three/addons/loaders/DRACOLoader.js";

const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(40, innerWidth / innerHeight, 0.1, 100);
camera.position.set(0, 1, 6);
scene.add(new THREE.HemisphereLight(0xffffff, 0x444444, 3));

// Draco圧縮したglbを読むにはDRACOLoaderを渡す(1つ作って使い回す)
const draco = new DRACOLoader().setDecoderPath(
  "https://cdn.jsdelivr.net/npm/three@0.170.0/examples/jsm/libs/draco/"
);
const loader = new GLTFLoader().setDRACOLoader(draco);

const clock = new THREE.Clock();
let mixer;

const gltf = await loader.loadAsync("logo.glb");
scene.add(gltf.scene);

// Blenderで付けた動きはgltf.animationsにクリップとして入っている
mixer = new THREE.AnimationMixer(gltf.scene);
gltf.animations.forEach((clip) => {
  const action = mixer.clipAction(clip);
  action.setLoop(THREE.LoopOnce, 1);
  action.clampWhenFinished = true; // 最後のフレームで止める
  action.play();
});

renderer.setAnimationLoop(() => {
  mixer?.update(clock.getDelta()); // 経過秒数で進める
  renderer.render(scene, camera);
});

AnimationMixerは、シーン内のオブジェクトのアニメーションを再生するプレーヤーで、毎フレームupdate()に経過秒数を渡して進めます。three.jsの最新版はr186(2026年9月24日)なので、新しく作る場合はバージョンの番号を差し替えてください。


glb書き出しでつまずく5つの点

Blenderの動きをglbで持ち出すときにつまずくのは、APIの変更、書き出されないアクション、上方向の違い、Draco圧縮、再生後の停止の5点です。どれもエラーが出ずに「動かない」「向きが違う」という形で現れるので、先に知っておくと原因を探す時間が減ります。

1. Blender 5.0でキーフレームの読み方が変わった

Blender 5.0で、アクションから直接Fカーブを取るaction.fcurvesが削除されました(5.0のリリースノート)。Blender 4.x向けの記事やAIが書くコードにはobj.animation_data.action.fcurvesがよく出てきますが、5.x では動きません。Fカーブは、アクションのスロットに対応するchannelbagから取ります。

import bpy
from bpy_extras import anim_utils

obj = bpy.data.objects["Logo"]
ad = obj.animation_data
# Blender 5.x:アクションのFカーブはスロットごとのchannelbagにある
bag = anim_utils.action_get_channelbag_for_slot(ad.action, ad.action_slot)
for fc in bag.fcurves:
    print(fc.data_path, fc.array_index, round(fc.evaluate(30), 3))

このコードで、サンプルの立方体の30フレーム目の値(高さ-1.025、回転3.062ラジアン)が読み出せることを確認しました。Claude Codeに頼むときは「Blender 5.2のAPIで」とバージョンを明記すると、古い書き方を避けやすくなります。

2. 動きが書き出されないアクションがある

BlenderのglTF 2.0マニュアルによると、既定のActionsモードでは、オブジェクトに付いているアクションか、NLAにstashしたアクションだけが書き出されます。マニュアルには「If you have multiple actions you want to export, make sure they are stashed!」とあります。サンプルのようにexport_animation_mode="SCENE"にすると、シーン全体の動きをフレームごとに焼き込んで1本のクリップにできます。

書き出せる動きの種類にも範囲があります。マニュアルでは、glTFのアニメーションはオブジェクトとボーンの位置・回転・拡大と、シェイプキーの値を変えるもの、と説明されています。マテリアルの色やライトの強さを動かしたい場合は、three.js側で付けるほうが確実です。

3. Blenderはz軸が上、glTFはy軸が上

Blenderはz軸が上の座標系で、glTFはglTF 2.0の仕様で「+Y as up」と決まっています。three.jsも既定の上方向はy軸です。書き出しのexport_yupは既定でオンなので、glbとthree.jsの間では向きは揃います。

ずれるのは、Blenderの座標の数値をJSに書き写すときです。Blenderの (x, y, z) は、three.jsでは (x, z, -y) になります。カメラの位置や回転の中心をBlenderから持ってくるときは、この入れ替えを忘れないようにしてください。

4. Draco圧縮したglbはDRACOLoaderがないと読めない

Draco圧縮は書き出しの既定ではオフで、export_draco_mesh_compression_enable=Trueで有効になります。圧縮したglbを読むには、three.jsのGLTFLoaderにsetDRACOLoader()でDRACOLoaderを渡す必要があります。渡さずに読むとエラーになります。

Dracoはファイルを小さくする代わりに、表示する端末側でデコードする時間がかかります。DRACOLoaderのドキュメントにも「at the cost of additional decoding time on the client device」と書かれています。モデルが小さい場合は、圧縮あり・なしの両方で読み込みの速さを測ってから決めてください。

5. 再生し終えたアクションは止まったままになる

LoopOnceで1回だけ再生したアクションは、最後まで行くと止まった状態になります。clampWhenFinishedを付けると最後のフレームで止まり、付けないと最初の姿勢に戻ります。もう一度再生したり、特定の時刻を表示したりする前にはaction.reset().play()で再生できる状態に戻します。


実例:orecticdesign.comのファーストビュー

筆者の個人サイトORECTICのファーストビューでは、真鍮の渾天儀が外側から集まって組み上がり、組み上がった後は輪が回り続けます。形と組み上がる動きはBlenderで作り、glbでthree.jsに渡しています(実物はorecticdesign.comで動いています)。Blenderは、筆者が作ったClaude Codeのスキルから画面なしで動かしました。

Blenderで試し描きした渾天儀。真鍮の輪と台座に囲まれて、中心に深緑の多面体がある
Blenderでの試し描き。この段階では中心の芯を深緑の多面体にしていた
orecticdesign.comのファーストビュー。真鍮の渾天儀の中心に地球儀があり、Works・Blog・Contactなどのナビのラベルが線で地球儀につながっている
公開しているファーストビュー。中心の地球儀とナビのラベルはthree.jsとHTMLの側で付けている

Blenderとthree.jsの役割分担

担当Blender(glbに入れる)three.js・HTML(ブラウザ側で付ける)
形輪・パネル・台座・芯の形とマテリアル中心の芯を、地図を貼った球に差し替え
動き外側から集まって組み上がる96フレーム(30fpsで3.2秒)組み上がった後に輪を回し続ける
光と空間試し描き用のライトとカメラ(glbには含めない)環境光・電灯の影・光のにじみ・霧・床と壁
文字なしナビのラベルはHTML。地球儀の回転に合わせて動かす

Blenderで付けたオブジェクトの名前は、glbの中にそのまま残ります。three.js側ではCoreという名前の芯を探して地図を貼った球に差し替え、名前がRingで始まる輪を回しています。差し替えや操作をしたい部品には、Blenderの段階で名前を付けておくと、ブラウザ側のコードが短くなります。

作ってみて分かったこと

  • 動きは動く状態で見て決める:組み上がりは、行き過ぎて揺れる動きを付けず、ぴたりと止まる形にした。動きの良し悪しは静止画では判断できないので、低解像度の動画で見てから決めている
  • ブラウザ側で付ける要素を先に決める:組み上がった後の回転をBlenderに入れると、ループの継ぎ目とファイルの重さが問題になりやすい。回り続ける動きはJSで1行足すほうが軽い
  • ファイルは小さく収まる:Draco圧縮したglbは約300KBだった。部品が多くても、動きを焼き込んだ3.2秒ならこの大きさで収まる
  • 動きを減らす設定にも対応する:OSで動きを減らす設定をしている人には、組み上がる動きを飛ばして完成形から表示している

書き出し先の選び方:3Dデータ・動画・CSS・SVG

Blenderで作った動きは、glb以外にも動画・CSS・SVGの線として書き出せます。何を動かすかで選びます。筆者は次の表で決めています。

動かすもの書き出し先理由
マウスやスクロールに反応させたい立体glb+three.jsカメラや部品をブラウザ側で動かせる
質感・光・モーションブラーが主役の素材(商品・ロゴ・背景ループ)動画(mp4・webm)レンダリングした見た目をそのまま出せる
文字・ラベル・ボタンなど、情報としての要素HTML/CSS文字を読める・選べる状態のまま動かせる
描かれる・伸びる線SVGの線線の太さを保ったまま拡大縮小できる

CSSで済む動きはどこまでか

位置・回転・拡大・透明度の変化だけなら、BlenderのFカーブの値をCSSのキーフレームに移せます。前の節のchannelbagからFカーブを読み、各フレームの値を書き出す流れです。ただ、文字やボタンを少し動かすだけなら、Blenderを通さずにClaudeにCSSを直接書かせるほうが早い場面も多くあります。その進め方はCSSアニメーションはAIに「コードを渡して」作るで、すぐ使える動きの例はCSSアニメーションのサンプル集で紹介しています。

透過動画はSafari向けに別の形式が要る

背景を透過させた動画を使う場合、webm(VP9)はSafariで透過が効きません。MDNのコーデックガイドにも「Safari does not support alpha transparency in this format」とあります。SafariはHEVCの透過動画に対応していますが、Blenderの動画書き出しではH.265/HEVCに透過を付けられないため、別のツールで変換する工程が必要です。透過させたい素材は、glbで持ち出せないかを先に検討する価値があります。


MCPを使う前に確認すること

どちらのMCPも、Claudeが書いたPythonを確認なしでBlenderの中で実行します。Blenderの中のPythonはファイルの読み書きもできるので、使う前に次の点を確認してください。

  • 公式のBlender MCPのページには「The MCP server will execute LLM generated code in Blender without any guards in place」とあり、機密情報のない環境(仮想マシンなど)での利用が勧められている
  • MCP for BlenderのREADMEは、Blenderのコードを実行するツールについて「ALWAYS save your work before using it」と注意している。作業中のファイルは先に保存しておく
  • MCP for Blenderのアドオンが開く接続には認証も暗号化もないとREADMEに書かれている。同じネットワークの他の端末から届かない環境で使う
  • MCP for Blenderは既定で匿名の利用状況を送る。止める場合はREADMEにある環境変数で無効にする

画面なしスクリプトの方式でも、実行するのはClaude Codeが書いたコードです。違いは、実行の前にファイルとして読めることです。Claude CodeのMCPの設定や使い分けはClaude CodeのMCP連携ガイド一覧に、同じ「公式コネクタとスクリプト」の比較をPhotoshopで試した例はClaudeでPhotoshopを操作する2つの方法にまとめています。


よくある質問

Q. ClaudeからBlenderを操作する公式の方法はありますか?

あります。Blenderの開発者によるBlender LabがBlender MCPを公開し、Anthropicが2026年4月28日にClaude向けの公式コネクタとして発表しました。Claude DesktopのCustomize → Connectorsから追加でき、Blender 5.1以上が必要です。

Q. Blender MCPとMCP for Blenderは何が違いますか?

Blender MCPはBlenderの開発者が作った公式のMCPサーバーで、MCP for Blender(旧blender-mcp)は個人開発者が作ったコミュニティ製のMCPサーバーです。どちらもClaudeから開いているBlenderを操作でき、AIが書いたPythonをBlenderの中で実行する点は同じです。

Q. MCPを使わずにClaude CodeからBlenderを動かせますか?

動かせます。Claude CodeにPythonスクリプトを書かせ、blender -b -P script.py で画面なしのBlenderに実行させます。シーンも動きもスクリプトに残るので、同じ素材を何度でも作り直せます。

Q. Blenderのアニメーションがglbに入らないのはなぜですか?

既定のActionsモードでは、オブジェクトに付いているアクションかNLAにstashしたアクションしか書き出されないためです。複数のアクションはstashしておくか、export_animation_mode=”SCENE” でシーン全体の動きを焼き込んで書き出します。

Q. three.jsで読み込んだglbの向きがずれるのはなぜですか?

Blenderはz軸が上で、glTFとthree.jsはy軸が上だからです。書き出しの export_yup が有効ならglbの向きは揃うので、ずれる場合はBlenderの座標の数値をJSに書き写した箇所を (x, z, -y) に直してください。

Q. Draco圧縮したglbが読み込めないときはどうすればいいですか?

GLTFLoaderにsetDRACOLoader()でDRACOLoaderを渡し、デコーダーの置き場所をsetDecoderPath()で指定してください。Draco圧縮したglbは、DRACOLoaderがないと読み込めません。


関連記事