Skip to content

はじめに(日本語)

自分で書いたフラグメントシェーダーを、そのまま本番のサイトに載せたい。@frapx/fx-canvas は、そのための小さなライブラリです。要素を 1 つ指定すると、その中に canvas を置いて、大きさを合わせながらシェーダーを描き続けます。

「シェーダーを 1 枚載せたいだけなのに、three.js ごと読み込むのは重い」——作り始めたのは、そう感じたのがきっかけです。同じ全画面のシェーダーを描くと、重さはこれくらい違います。

ライブラリ min+gzip
@frapx/fx-canvas(@frapx/core 込み) 11 kB
Babylon.js 45 kB
three.js 133 kB

2026-10-30 計測 · 測り方

あとから SDF やノイズ、レイマーチ、マルチパス、パーティクル、動画の書き出しが欲しくなったら、部品を足すだけで使えます。モデルやライトを扱う本格的な 3D なら、three.js や Babylon.js を使ってください。

本番で地味に面倒なところは、fx-canvas がやってくれます。

  • 画面外や非表示のタブでは、描画を止める
  • 「視差効果を減らす」設定の人には、既定で動きを止める(respectReducedMotion)
  • SSR で import しても壊れない
  • WebGL1 でも動く(WebGL2 のシェーダーも書ける)

このドキュメントは英語版が正です。日本語はこの入口のページだけで、各パッケージの詳しい説明は英語のページにリンクしています。

Terminal window
pnpm add @frapx/fx-canvas
import { createShaderBackground, glsl } from "@frapx/fx-canvas";
createShaderBackground({
target: ".hero",
fragment: glsl`
precision highp float;
uniform vec2 u_resolution;
uniform vec2 u_pointerUv;
uniform float u_time;
void main() {
vec2 uv = gl_FragCoord.xy / u_resolution;
float glow = 0.5 + 0.5 * sin(u_time + uv.x * 8.0);
gl_FragColor = vec4(uv.x, u_pointerUv.y, glow, 1.0);
}
`
});

target には CSS セレクターか要素を渡します。canvas はその要素の大きさに追従するので、要素には高さを付けておいてください。

u_time(秒)、u_resolution(描画バッファの px)、u_pointerUv(ポインター位置、0〜1)などはライブラリが毎フレーム渡します。一覧は@frapx/fx-canvas のリファレンスにあります。

自分の uniform は JS 側では u_ を付けずに、GLSL 側では u_ を付けて書きます。

const fx = createShaderBackground({
target: ".hero",
fragment, // uniform float u_progress; を宣言しておく
uniforms: { progress: 0 }
});
fx.setUniform("progress", 0.4);

OS で「視差効果を減らす」(Windows では「アニメーション効果」をオフ)にしている人には、既定で 1 枚の静止画を表示したままにします。設定が戻れば、アニメーションも再開します(respectReducedMotion、既定値は true)。

止めずに動きを弱めたい場合は、respectReducedMotion: false を渡して、シェーダーで u_reducedMotion を読みます。値はこのフラグに関係なく常に渡されます(設定がオンなら 1、オフなら 0)。

uniform float u_reducedMotion;
// 例: reduced motion のときは揺れ幅を 1 割にする
float amount = mix(1.0, 0.1, u_reducedMotion);

画面外に出たときと、タブが非表示のときの停止は既定で有効です(pauseWhenOffscreen / pauseWhenHidden)。自分で何かを書く必要はありません。

@frapx/react-canvas を追加して、<ShaderBackground /> コンポーネントを使います。

Terminal window
pnpm add @frapx/fx-canvas @frapx/react-canvas
import { ShaderBackground } from "@frapx/react-canvas";
import { glsl } from "@frapx/fx-canvas";
const fragment = glsl`
precision highp float;
uniform vec2 u_resolution;
uniform float u_time;
void main() {
vec2 uv = gl_FragCoord.xy / u_resolution;
gl_FragColor = vec4(uv, 0.5 + 0.5 * sin(u_time), 1.0);
}
`;
export function HeroBackground() {
return (
<ShaderBackground
fragment={fragment}
className="hero-bg"
/>
);
}

アンマウント時の後片付けはコンポーネントが行います。フックで使う useShaderBackground() もあります。

Astro では、コンポーネントの <script> の中で呼びます。<script> はブラウザで動くので、frontmatter(--- の間)には書きません。frontmatter で import しても SSR は壊れませんが、そこでは何も描かれません。

<section class="hero">
<div class="hero-content">
<h1>Hello</h1>
</div>
</section>
<style>
.hero {
min-height: 60vh;
}
.hero-content {
position: relative;
z-index: 1;
}
</style>
<script>
import { createShaderBackground, glsl } from "@frapx/fx-canvas";
createShaderBackground({
target: ".hero",
fragment: glsl`
precision highp float;
uniform vec2 u_resolution;
uniform float u_time;
void main() {
vec2 uv = gl_FragCoord.xy / u_resolution;
gl_FragColor = vec4(uv, 0.5 + 0.5 * sin(u_time), 1.0);
}
`
});
</script>

canvas は .hero の最初の子として、position: absolute; z-index: 0 で差し込まれます。文字などの中身は、上の .hero-content のように position: relative; z-index: 1 を付けて canvas の上に出します。