「コーディング不要で流体エフェクト」って本当? three-fluid-fx を Framer で試してみました

「コーディング不要で流体エフェクト」って本当? three-fluid-fx を Framer で試してみました

「Three.js で流体エフェクトを コーディング不要 で追加できる」——そんな紹介を見かけて、three-fluid-fx というライブラリを触ってみました。先に結論をお伝えすると、ライブラリ自体はとてもよくできています。ただ「コーディング不要」という表現は、実際に使ってみると少しニュアンスが違いました。そして我々が一番気になっていた「Framer のサイトに載せられるか」については、ちゃんと載せられます。ただ、Framer ならではの "つまずきポイント" が3つほどあったので、そのあたりも順番に共有していきますね。

このライブラリは何者か(まずは事実確認から)

最初に素性を整理しておきます。今回の検証時点で確認できたのは、こんなところでした。

  • リポジトリは artcodev/three-fluid-fx、ライセンスは MIT、作者は Artem Korenevych さんです。

  • npm の最新は three-fluid-fx@0.1.0three を peer dependency として持っていて、要求バージョンは >=0.183.0(手元では three@0.184.0 で動作確認しました)。

  • WebGL/GLSL パイプラインと WebGPU/TSL パイプラインの両方を同梱しています。

  • バンドルは軽めで、全パス込みの GLSL 版で約13KB(gzip)、TSL 版で約11KB。three 本体は同梱せず peer 依存のままです。

  • 成熟度はまだこれからという段階です。GitHub のスターは約100、フォークは十数件、正式リリースタグはまだなく、バージョンも 0.1.0 でした。

ひとつ押さえておきたいのは、これは新しい流体シミュレーションのアルゴリズムではない、という点です。作者さん自身が README で正直に書かれているのですが、中身は Jos Stam の Stable Fluids(SIGGRAPH 1999)に渦度補正(Fedkiw 2001)とオプションの BFECC を足した、20年以上前から広く実装されてきた手法を Three.js 向けにパッケージし直したもの です。つまり価値は新規性ではなく、「Three.js プロジェクトへの組み込みの手間を消してくれたこと」にあります。ここはむしろ好印象でした。

試してみた1:「コーディング不要」の実際のところ

「コーディング不要」というのは、正直に言うと少し盛り気味かなと思います。ただ、ここは公平にお伝えしておきたいのですが、作者さん本人の表現はちゃんと正確 でした。公式サイトの言い回しは「ボイラープレートを書かずに(without writing boilerplate)」であって、「コードを書かずに」ではありません。「コーディング不要」というのは、おそらく二次的な紹介文の側で少し強めに表現されたものだと思います。

実際の使い方はこんな感じになります。ソルバーを作って、step() をループで回して、出力されたテクスチャを 自分のシェーダーで画面に合成する。最小構成でもこれくらいは書くことになります。

import { ShaderMaterial, Timer, Uniform, WebGLRenderer } from "three"
import {
  FluidSimulation,
  attachPointerSplats,
  FULLSCREEN_VERTEX,
  FullscreenPass,
} from "three-fluid-fx"

const renderer = new WebGLRenderer({ alpha: true, premultipliedAlpha: false })
const fluid = new FluidSimulation(renderer, { profile: "balanced" })
attachPointerSplats(renderer.domElement, fluid) // ポインタ追従はヘルパーにお任せ

// density テクスチャを画面に出すのは「自前のシェーダー」の役割です
const mat = new ShaderMaterial({
  vertexShader: FULLSCREEN_VERTEX,
  fragmentShader: `
    precision highp float;
    varying vec2 vUv;
    uniform sampler2D tFluid;
    void main() {
      vec3 f = texture2D(tFluid, vUv).rgb;
      gl_FragColor = vec4(f, clamp(f.b * 2.0, 0.0, 1.0));
    }
  `,
  uniforms: { tFluid: new Uniform(fluid.densityTexture) },
})
const pass = new FullscreenPass(mat)

const clock = new Timer()
renderer.setAnimationLoop(() => {
  clock.update()
  fluid.step(clock.getDelta())
  pass.render(renderer, null)
})

というわけで実際のところは「5行くらいの API + 自前のシェーダー合成」で、完全なノーコードではありません。とはいえ、「シェーダーや EffectComposer の配線を一から書く大変さを肩代わりしてくれる」と考えると、十分にありがたいツールです。Three.js と GLSL の基礎がある方なら、導入の手間はぐっと減ると思います。

🎥 動画①:黒背景に乗せた流体エフェクト本体。カーソルの動きに液体が追従します。(このあと紹介する HTML Embed 版・Code Component 版は、どちらも見た目はこの系統になります)

試してみた2:Framer に載せられるか

ここが本題です。VISK サイトは Framer で作っているので、ここに載るかどうかが実用上の分かれ目でした。3つのルートを試してみて、どれもちゃんと動きました。

ルートA:Embed(HTML 埋め込み)

いちばん隔離されていて、事故が少ない方法です。Framer の Embed に、importmapthreethree-fluid-fx を読む完結 HTML を貼るだけ。iframe のように動くので、Framer 本体のランタイムと干渉しません。互換性を優先するなら WebGL 版が無難です(WebGPU/TSL 版は対応ブラウザが必要になります)。

ルートB:Code Component(区画の中に収める)

React コンポーネントにして、プロパティで色・強度・品質を調整できるようにした版です。esm.sh から読み込みます。ここで最初につまずいたのがサイズでした。 コンポーネントの中身を height:100% で組むと、Framer 側のサイズ指定が「Fit(中身に合わせる)」のままだと高さが 0 に潰れてしまって、何も表示されません。Width/Height をはっきり固定してあげれば解決します。

補足:見た目はルートAとほぼ同じになります。違いは "絵面" ではなく "入れ方"(Embed か Code Component か)なので、上の動画①のイメージで通じるかと思います。

ルートC:サイト全面オーバーレイ(クリックは下に通す)

「サイトのコンテンツの上に、カーソル追従のトレイルを重ねたい」という用途です。これは背景を透過にするだけだと足りなくて、もうひと工夫いりました。

  1. クリックを背後に通してあげる。 全面に被せたキャンバスは、そのままだとクリックもスクロールも全部受け止めてしまいます。それだとサイトのボタンが押せなくなってしまうので、キャンバスを pointer-events: none にします。

  2. ポインタは別ルートで拾う。 pointer-events: none にするとキャンバス自身はイベントを受け取らないので、代わりに window 側でポインタの位置を拾って、流体に流し込みます。

canvas.style.pointerEvents = "none"      // クリック/スクロールは背後のサイトへ
document.body.appendChild(canvas)        // ← 後で出てくる "つまずき③" の対策で body 直下に
window.addEventListener("pointermove", onMove, { capture: true })

onMove の中身は、ライブラリの attachPointerSplats の実装を読んで同じ式を移植しました。座標を UV に変換して fluid.addSplat(x, y, dx * force, dy * force) を呼んであげるだけです。

🎥 動画②:サイトのコンテンツに透過オーバーレイした様子。エフェクトが上に乗っていても、背後のボタンやスクロールはそのまま操作できているのがポイントです。

Framer での "つまずきポイント" 3つ

検証してみて、Framer ならではでつまずいたところは、だいたい次の3つにまとまりました。これから試す方の時間節約になればうれしいです。

つまずき①:編集キャンバスでは WebGL が動かない。 Framer のデザイン画面に置いても、何も動かないことがあります。なので必ず Preview(再生)か公開ページ で確認してください。「配置したのに動かない…」のほとんどはこれです(私もこれで一度ハマりました)。

つまずき②:three が二重に読み込まれる。 Framer は three を内蔵していますが、three-fluid-fx が別バージョンの three を読むと、three が2つロードされてしまって、ほぼ確実に動かなくなります。esm.sh で読むときは ?deps=three@0.184.0(または ?external=three)を付けて、three のインスタンスを1つにそろえてあげましょう。

import { WebGLRenderer } from "https://esm.sh/three@0.184.0"
import { FluidSimulation } from "https://esm.sh/three-fluid-fx@0.1.0?deps=three@0.184.0"

つまずき③:transform の下では position: fixed が効かない。 全面オーバーレイを position: fixed で組むとき、Framer は親要素に transform をかけることがあります。transform を持つ祖先がいると fixed の基準が画面ではなくその祖先になってしまい、固定が崩れます。対策として、キャンバスを document.body の直下に逃がしてあげるとうまくいきました。

ソースを読んで気づいた小ネタ

density テクスチャを描画する構成(上の最小構成)では、attachPointerSplatscoloredStrokes オプションは 見た目にはほとんど効いていません でした。色付けは dye チャンネル経由なのですが、density 描画ではそのチャンネルを参照していないためです。では画面に出ているトレイルの色は何かというと、速度場(velocity)から来ている色 でした。トレイルの色味が動きの向きで変わって見えるのは、このためなんですね。色を自分でコントロールしたい場合は、dye チャンネルを有効にするか、合成シェーダー側で着色してあげる必要があります。

どんな場面に向いていそうか(所感)

ここからは検証を踏まえた、あくまで個人的な所感です。

向いていそうなのは、ヒーローセクションの背景演出や、インタラクティブなデモ・ランディングページかなと思います。導入が軽くて、バンドルも小さくて、見栄えはしっかり派手です。Three.js を触れる方なら、短い時間で組めると思います。

一方で、少し気をつけたいのが コーポレートサイトの全ページに常時オーバーレイ するケースです。実際に VISK サイトに重ねてみたのですが、本文の上にエフェクトが乗ると、やはり読みやすさは少し下がりました。あわせて、アニメーションのループが回り続けるので、モバイルでは GPU やバッテリーへの負荷も気にしておきたいところです。本番で使うなら「トップのヒーロー区画だけにする」「品質プロファイルを軽量側にする」といった割り切りが現実的かなと思います。

それと、用途を考えるうえで作者さんの注記も押さえておくとよさそうです。これは CFD レベルの物理精度を狙うものではありませんし、しぶきを伴う本格的な水面表現や、3D の煙・炎のようなボリューム表現でもありません。あくまで2Dの、リアルタイムの「見た目」に振り切った VFX ツールという位置づけです。

おまけ:社内で見せてみたら、あっさりボツになりました

透過オーバーレイ版がうまく動いたので、試しに VISK サイトに実際に反映して、隣の席の同僚に「これ、どう?」と見せてみました。

返ってきた第一声は——「いや、絶対あかんでしょ」。

……。本文の上を液体がずっと流れ続ける画面は、見ているぶんには楽しいのですが、いざ自社サイトで常用するとなると、やっぱり刺激が強すぎたようです。

というわけで、今回のサイト全面オーバーレイ版は、ひとまずお蔵入りとなりそうです。とはいえ、検証としては収穫が多くて、ライブラリの素性・Framer 組み込みの勘どころ・つまずきポイントまで、ひととおり手を動かして確かめられました。こういう「やってみて、ボツにする」判断ができるのも検証のいいところかなと思っています。どこかで "ここぞ" という演出の出番があれば、引き出しから出してこようと思います。

まとめ

  • three-fluid-fx は実在する MIT ライセンスのライブラリです。Three.js 向けに、既存の流体アルゴリズムを 組み込みやすくパッケージ してくれたものでした。

  • 「コーディング不要」は少し盛り気味で、実際は「ボイラープレート不要」。5行 API + 自前シェーダー合成が必要です。作者さん本人の表現は正確でした。

  • Framer にはちゃんと載ります。Embed / 区画コンポーネント / 全面オーバーレイ の3パターンで動作確認できました。

  • Framer ならではのつまずきは3つ。①Preview でしか動かない ②three の二重ロード(?deps で回避)③transform の下で fixed が崩れる(body 直下に逃がす)。

  • 成熟度は 0.1.0 とまだこれから。手軽で映えるので検証・デモ用途には良いですが、本番投入は用途を絞って慎重に。今回の全面適用版は、社内レビューであっさりお蔵入りになりました。

検証環境:three-fluid-fx@0.1.0 / three@0.184.0 / Framer。記載は検証時点の確認結果です。バージョンやライブラリの仕様は今後変わる可能性があります。

「Three.js で流体エフェクトを コーディング不要 で追加できる」——そんな紹介を見かけて、three-fluid-fx というライブラリを触ってみました。先に結論をお伝えすると、ライブラリ自体はとてもよくできています。ただ「コーディング不要」という表現は、実際に使ってみると少しニュアンスが違いました。そして我々が一番気になっていた「Framer のサイトに載せられるか」については、ちゃんと載せられます。ただ、Framer ならではの "つまずきポイント" が3つほどあったので、そのあたりも順番に共有していきますね。

このライブラリは何者か(まずは事実確認から)

最初に素性を整理しておきます。今回の検証時点で確認できたのは、こんなところでした。

  • リポジトリは artcodev/three-fluid-fx、ライセンスは MIT、作者は Artem Korenevych さんです。

  • npm の最新は three-fluid-fx@0.1.0three を peer dependency として持っていて、要求バージョンは >=0.183.0(手元では three@0.184.0 で動作確認しました)。

  • WebGL/GLSL パイプラインと WebGPU/TSL パイプラインの両方を同梱しています。

  • バンドルは軽めで、全パス込みの GLSL 版で約13KB(gzip)、TSL 版で約11KB。three 本体は同梱せず peer 依存のままです。

  • 成熟度はまだこれからという段階です。GitHub のスターは約100、フォークは十数件、正式リリースタグはまだなく、バージョンも 0.1.0 でした。

ひとつ押さえておきたいのは、これは新しい流体シミュレーションのアルゴリズムではない、という点です。作者さん自身が README で正直に書かれているのですが、中身は Jos Stam の Stable Fluids(SIGGRAPH 1999)に渦度補正(Fedkiw 2001)とオプションの BFECC を足した、20年以上前から広く実装されてきた手法を Three.js 向けにパッケージし直したもの です。つまり価値は新規性ではなく、「Three.js プロジェクトへの組み込みの手間を消してくれたこと」にあります。ここはむしろ好印象でした。

試してみた1:「コーディング不要」の実際のところ

「コーディング不要」というのは、正直に言うと少し盛り気味かなと思います。ただ、ここは公平にお伝えしておきたいのですが、作者さん本人の表現はちゃんと正確 でした。公式サイトの言い回しは「ボイラープレートを書かずに(without writing boilerplate)」であって、「コードを書かずに」ではありません。「コーディング不要」というのは、おそらく二次的な紹介文の側で少し強めに表現されたものだと思います。

実際の使い方はこんな感じになります。ソルバーを作って、step() をループで回して、出力されたテクスチャを 自分のシェーダーで画面に合成する。最小構成でもこれくらいは書くことになります。

import { ShaderMaterial, Timer, Uniform, WebGLRenderer } from "three"
import {
  FluidSimulation,
  attachPointerSplats,
  FULLSCREEN_VERTEX,
  FullscreenPass,
} from "three-fluid-fx"

const renderer = new WebGLRenderer({ alpha: true, premultipliedAlpha: false })
const fluid = new FluidSimulation(renderer, { profile: "balanced" })
attachPointerSplats(renderer.domElement, fluid) // ポインタ追従はヘルパーにお任せ

// density テクスチャを画面に出すのは「自前のシェーダー」の役割です
const mat = new ShaderMaterial({
  vertexShader: FULLSCREEN_VERTEX,
  fragmentShader: `
    precision highp float;
    varying vec2 vUv;
    uniform sampler2D tFluid;
    void main() {
      vec3 f = texture2D(tFluid, vUv).rgb;
      gl_FragColor = vec4(f, clamp(f.b * 2.0, 0.0, 1.0));
    }
  `,
  uniforms: { tFluid: new Uniform(fluid.densityTexture) },
})
const pass = new FullscreenPass(mat)

const clock = new Timer()
renderer.setAnimationLoop(() => {
  clock.update()
  fluid.step(clock.getDelta())
  pass.render(renderer, null)
})

というわけで実際のところは「5行くらいの API + 自前のシェーダー合成」で、完全なノーコードではありません。とはいえ、「シェーダーや EffectComposer の配線を一から書く大変さを肩代わりしてくれる」と考えると、十分にありがたいツールです。Three.js と GLSL の基礎がある方なら、導入の手間はぐっと減ると思います。

🎥 動画①:黒背景に乗せた流体エフェクト本体。カーソルの動きに液体が追従します。(このあと紹介する HTML Embed 版・Code Component 版は、どちらも見た目はこの系統になります)

試してみた2:Framer に載せられるか

ここが本題です。VISK サイトは Framer で作っているので、ここに載るかどうかが実用上の分かれ目でした。3つのルートを試してみて、どれもちゃんと動きました。

ルートA:Embed(HTML 埋め込み)

いちばん隔離されていて、事故が少ない方法です。Framer の Embed に、importmapthreethree-fluid-fx を読む完結 HTML を貼るだけ。iframe のように動くので、Framer 本体のランタイムと干渉しません。互換性を優先するなら WebGL 版が無難です(WebGPU/TSL 版は対応ブラウザが必要になります)。

ルートB:Code Component(区画の中に収める)

React コンポーネントにして、プロパティで色・強度・品質を調整できるようにした版です。esm.sh から読み込みます。ここで最初につまずいたのがサイズでした。 コンポーネントの中身を height:100% で組むと、Framer 側のサイズ指定が「Fit(中身に合わせる)」のままだと高さが 0 に潰れてしまって、何も表示されません。Width/Height をはっきり固定してあげれば解決します。

補足:見た目はルートAとほぼ同じになります。違いは "絵面" ではなく "入れ方"(Embed か Code Component か)なので、上の動画①のイメージで通じるかと思います。

ルートC:サイト全面オーバーレイ(クリックは下に通す)

「サイトのコンテンツの上に、カーソル追従のトレイルを重ねたい」という用途です。これは背景を透過にするだけだと足りなくて、もうひと工夫いりました。

  1. クリックを背後に通してあげる。 全面に被せたキャンバスは、そのままだとクリックもスクロールも全部受け止めてしまいます。それだとサイトのボタンが押せなくなってしまうので、キャンバスを pointer-events: none にします。

  2. ポインタは別ルートで拾う。 pointer-events: none にするとキャンバス自身はイベントを受け取らないので、代わりに window 側でポインタの位置を拾って、流体に流し込みます。

canvas.style.pointerEvents = "none"      // クリック/スクロールは背後のサイトへ
document.body.appendChild(canvas)        // ← 後で出てくる "つまずき③" の対策で body 直下に
window.addEventListener("pointermove", onMove, { capture: true })

onMove の中身は、ライブラリの attachPointerSplats の実装を読んで同じ式を移植しました。座標を UV に変換して fluid.addSplat(x, y, dx * force, dy * force) を呼んであげるだけです。

🎥 動画②:サイトのコンテンツに透過オーバーレイした様子。エフェクトが上に乗っていても、背後のボタンやスクロールはそのまま操作できているのがポイントです。

Framer での "つまずきポイント" 3つ

検証してみて、Framer ならではでつまずいたところは、だいたい次の3つにまとまりました。これから試す方の時間節約になればうれしいです。

つまずき①:編集キャンバスでは WebGL が動かない。 Framer のデザイン画面に置いても、何も動かないことがあります。なので必ず Preview(再生)か公開ページ で確認してください。「配置したのに動かない…」のほとんどはこれです(私もこれで一度ハマりました)。

つまずき②:three が二重に読み込まれる。 Framer は three を内蔵していますが、three-fluid-fx が別バージョンの three を読むと、three が2つロードされてしまって、ほぼ確実に動かなくなります。esm.sh で読むときは ?deps=three@0.184.0(または ?external=three)を付けて、three のインスタンスを1つにそろえてあげましょう。

import { WebGLRenderer } from "https://esm.sh/three@0.184.0"
import { FluidSimulation } from "https://esm.sh/three-fluid-fx@0.1.0?deps=three@0.184.0"

つまずき③:transform の下では position: fixed が効かない。 全面オーバーレイを position: fixed で組むとき、Framer は親要素に transform をかけることがあります。transform を持つ祖先がいると fixed の基準が画面ではなくその祖先になってしまい、固定が崩れます。対策として、キャンバスを document.body の直下に逃がしてあげるとうまくいきました。

ソースを読んで気づいた小ネタ

density テクスチャを描画する構成(上の最小構成)では、attachPointerSplatscoloredStrokes オプションは 見た目にはほとんど効いていません でした。色付けは dye チャンネル経由なのですが、density 描画ではそのチャンネルを参照していないためです。では画面に出ているトレイルの色は何かというと、速度場(velocity)から来ている色 でした。トレイルの色味が動きの向きで変わって見えるのは、このためなんですね。色を自分でコントロールしたい場合は、dye チャンネルを有効にするか、合成シェーダー側で着色してあげる必要があります。

どんな場面に向いていそうか(所感)

ここからは検証を踏まえた、あくまで個人的な所感です。

向いていそうなのは、ヒーローセクションの背景演出や、インタラクティブなデモ・ランディングページかなと思います。導入が軽くて、バンドルも小さくて、見栄えはしっかり派手です。Three.js を触れる方なら、短い時間で組めると思います。

一方で、少し気をつけたいのが コーポレートサイトの全ページに常時オーバーレイ するケースです。実際に VISK サイトに重ねてみたのですが、本文の上にエフェクトが乗ると、やはり読みやすさは少し下がりました。あわせて、アニメーションのループが回り続けるので、モバイルでは GPU やバッテリーへの負荷も気にしておきたいところです。本番で使うなら「トップのヒーロー区画だけにする」「品質プロファイルを軽量側にする」といった割り切りが現実的かなと思います。

それと、用途を考えるうえで作者さんの注記も押さえておくとよさそうです。これは CFD レベルの物理精度を狙うものではありませんし、しぶきを伴う本格的な水面表現や、3D の煙・炎のようなボリューム表現でもありません。あくまで2Dの、リアルタイムの「見た目」に振り切った VFX ツールという位置づけです。

おまけ:社内で見せてみたら、あっさりボツになりました

透過オーバーレイ版がうまく動いたので、試しに VISK サイトに実際に反映して、隣の席の同僚に「これ、どう?」と見せてみました。

返ってきた第一声は——「いや、絶対あかんでしょ」。

……。本文の上を液体がずっと流れ続ける画面は、見ているぶんには楽しいのですが、いざ自社サイトで常用するとなると、やっぱり刺激が強すぎたようです。

というわけで、今回のサイト全面オーバーレイ版は、ひとまずお蔵入りとなりそうです。とはいえ、検証としては収穫が多くて、ライブラリの素性・Framer 組み込みの勘どころ・つまずきポイントまで、ひととおり手を動かして確かめられました。こういう「やってみて、ボツにする」判断ができるのも検証のいいところかなと思っています。どこかで "ここぞ" という演出の出番があれば、引き出しから出してこようと思います。

まとめ

  • three-fluid-fx は実在する MIT ライセンスのライブラリです。Three.js 向けに、既存の流体アルゴリズムを 組み込みやすくパッケージ してくれたものでした。

  • 「コーディング不要」は少し盛り気味で、実際は「ボイラープレート不要」。5行 API + 自前シェーダー合成が必要です。作者さん本人の表現は正確でした。

  • Framer にはちゃんと載ります。Embed / 区画コンポーネント / 全面オーバーレイ の3パターンで動作確認できました。

  • Framer ならではのつまずきは3つ。①Preview でしか動かない ②three の二重ロード(?deps で回避)③transform の下で fixed が崩れる(body 直下に逃がす)。

  • 成熟度は 0.1.0 とまだこれから。手軽で映えるので検証・デモ用途には良いですが、本番投入は用途を絞って慎重に。今回の全面適用版は、社内レビューであっさりお蔵入りになりました。

検証環境:three-fluid-fx@0.1.0 / three@0.184.0 / Framer。記載は検証時点の確認結果です。バージョンやライブラリの仕様は今後変わる可能性があります。