dwgx@blog:~$dwgx
> cd ../posts

Shadertoy の shader を抜き取る小さなツール ShadertoyTool

// created

ShadertoyTool は単一ファイルの Python CLI だ。Shadertoy の URL か 6 桁の ID を投げ込むと、shader をソースコードごとテクスチャ・音声・動画まで一括で抜き取ってくれる。ウェブページ上では効果を眺められるだけで、入力リソースを全部揃えようとすると手動で Cloudflare を回避する必要があって面倒なので、いっそ自動化した。

すべての Render Pass(Image / Buffer / Sound / Common)を 1 つの .glsl にまとめて書き出し、各 pass の前にコメントヘッダを付けて出所をはっきり示す。

// ShaderToy ID: XltGDr
// Name: Contra
// Retrieved: 2025-01-01 12:00:00 UTC

// ===== Pass 0: Image (image) =====
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    // ...
}

// ===== Pass 1: Buffer_A (buffer) =====
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    // ...
}

参照しているテクスチャ・音声・動画・Cubemap は全部ダウンロードして <shader_id>_assets/ に入れ、さらに manifest.json を生成して各リソースの URL、ファイル名、ダウンロード状態、参照位置を記録する。これで shader 一式の現場がまるごと揃う。

使い方はシンプルだ。

python fetch_shadertoy.py https://www.shadertoy.com/view/4djyRD
# あるいは ID を直接渡す
python fetch_shadertoy.py 4djyRD

ソースだけでリソースはいらない場合は --skip-assets を付け、出力ディレクトリを変えるには -o を使う。Windows には fetch_shadertoy.cmd があり、まず requestsbrowser_cookie3playwright がインストール済みかを確認し、足りないものは pip で自動補完してから実行する。

本当に骨が折れるのは shader のパースではなく、いかに安定してデータを取ってくるかだ。Shadertoy の前には Cloudflare が立ちはだかっていて、直接リクエストするとしょっちゅう門前払いを食らう。そこで、いちばん手軽なものからいちばん力技のものまで、3 段階の fallback を用意した。

  1. API v1:アプリを作って key を取得し、公式インターフェース /api/v1/shaders/<id>?key=<key> を通す。Cloudflare を完全に回避でき、いちばん安定するので、一般的には key を設定しておくのを推奨する。
  2. Legacy POST:key がない場合は、ウェブクライアントを模して https://www.shadertoy.com/shadertoy に内部リクエストを送る。Cookie は browser-cookie3 でローカルのブラウザ(Chrome / Edge / Brave / Firefox)から抜き出す。
  3. ブラウザ fallback:前の 2 つがどちらもダメなら、Playwright でローカルの Edge を立ち上げて認証を突破し、ページ内で fetch を使って同じインターフェースを叩く。--headless も可能だ。

リソースのダウンロードも同じ発想で、まず requests で直接引っ張り、403 や Cloudflare に阻まれたものはブラウザコンテキストに投げ込んでリトライし、408/429/500 に当たれば自動でリトライする。スクレイピングは Shadertoy の内部インターフェースと Cloudflare の顔色次第なので、成功率はサイトの状況に応じて上下する。だから key を設定した経路を常に最優先にする。依存は 3 つだけ:requestsbrowser-cookie3playwright、Python 3.8+ で動く。