🧑‍🎨

振り子の物理がアートになるとき:Pollock風ジェネラティブアートの実装

に公開

Paintボタンを押すと、毎回異なるパターンが生成されます。
上の埋め込みでは見切れているので、大きなウィンドウでCodePenのサイトを開いて、触ってみることをおすすめします:https://codepen.io/kokoyoshi/pen/YPWVwPB

ジェネレータの生成例
ジェネレータの生成例

はじめに

ジャクソン・ポロックをご存知でしょうか。

キャンバスを床に置いて、上からペンキを垂らす。20世紀アメリカを代表するこの画家の技法は「ドリッピング」と呼ばれています(こんな絵です)。

一見するとランダムに絵の具を撒き散らしているだけに見えるかもしれません。でも実は、Pollockは紐で吊るした容器を振り子のように振って描いている場合もありました。振り子だから物理法則に従います。端で減速すればペンキが溜まり、中央で加速すれば薄く伸びる。

さらに面白いことに、1999年に物理学者リチャード・テイラーが Nature で発表した研究によると、この技法で生まれるパターンはフラクタル(自然界に見られる自己相似構造)だったそうです。そしてこのフラクタルを見ると、人間のストレスが60%軽減されるという研究結果もあるそうです。

振り子の物理 → フラクタル → 心地よさ。

この連鎖が面白いと思って、コードで再現してみました。本稿の実装はあくまでも一例ですので、参考程度に見てもらえればと思います。

物理現象を分解する

というわけで、振り子の動きから生まれる視覚的特徴を整理してみます。

物理現象 視覚的特徴
振り子の端で減速 線が太く、濃い(ペンキが溜まる)
振り子の中央で加速 線が細く、薄い(ペンキが伸びる)
遠心力 飛沫が飛び散る
複数回の往復 軌跡が重なる
複数の色で繰り返す レイヤーの重なり

これをコードで再現していきます。

実装:振り子の軌跡

基本の動き

まず、振り子の速度変化を再現します。

const speed = baseSpeed * (0.3 + Math.abs(Math.sin(phase)) * 0.7);

sin(phase) で速度に緩急をつけています。端(sin=0)で遅く、中央(sin=1)で速い。振り子の運動方程式そのものですね。

速度から太さ・透明度へ

速度を計算したら、それを視覚属性に変換します。

// 遅いほど太く
const curve = Math.pow(1 - normalizedVelocity, 1.5);
thickness = lerp(minThickness, maxThickness, curve);

// 遅いほど濃く
opacity = lerp(0.05, 1.0, 1 - normalizedVelocity);

非線形カーブ(1.5乗)を使っているのは、線形だと差が出にくいように感じたからです。ここは見て調整しています。

境界での折り返し

振り子は端で方向を変えます。キャンバスの境界を超えたら、中心に向かって戻ってくるようにします。

if (isOutsideBounds(x, y)) {
  // 中心への角度を計算
  const toCenter = Math.atan2(centerY - y, centerX - x);
  // 少しランダム性を加えて新しい方向へ
  moveAngle = toCenter + randomRange(-0.25, 0.25) * Math.PI;
}

ポイントは、キャンバス内で折り返すのではなく、外に出てから戻ってくること。これで「端から端まで一気に振る」ストロークが生まれます。

飛沫(drips)

ストロークだけだと少し物足りなく感じたので、飛沫も追加しています。振り子の勢いでペンキが飛び散る表現です。確率的に発生させて、ベクトル方向に縮小しながら描画します。

// 確率で飛沫を生成
if (Math.random() < 0.002 && isInsideCanvas(x, y)) {
  drips.push(createDrip(x, y));
}

// 飛沫の描画:縮小しながら連続して円を描く
while (radius > 0.5) {
  drawCircle(x, y, radius);
  x += vx * 0.1;
  y += vy * 0.1;
  radius *= 0.85;
}

レイヤーの重ね塗り

Pollockは複数の色を重ねて塗っていました。黒、赤、青、黄、白あたりがよく使われていたようです。

const layers = [
  { color: '#1a1a1a' },  // 黒
  { color: '#c0392b' },  // 赤
  { color: '#6e3b1a' },  // バーントアンバー
  { color: '#2980b9' },  // 青
  { color: '#f1c40f' },  // 黄
  { color: '#ffffff' },  // 白
];

ここで mix-blend-mode: darken を使っています。暗い色が勝つブレンドモードで、絵の具のような色の重なりが出せます。

微調整の話

実装の大部分は物理の再現なんですが、パラメータの調整は「見て決める」しかないのかなと思います。

たとえば白い線の太さ。最初は他の色と同じにしていたんですが、目立ちすぎるように感じました。

1/3に → まだ太い
1/6に → 細すぎる
1/5に → ちょうどいい

この「ちょうどいい」は理屈では導けなくて、見て、感じて、調整する、の繰り返しです。

揺らぎ(wobble)の大きさも同様で、大きすぎると「必要以上に震えてる」、小さすぎると「機械的」な印象になります。0.25〜0.65pxという微妙な範囲に落ち着いたのは、何度も見比べた結果です。

結局、コードは物理現象を再現できるけど、「これでいい」を決めるのは人間の目と感覚なんだなと思います。

おわりに

振り子の物理 → フラクタル → 視覚との共鳴 → 心地よさ

Pollockがやっていたのは、この連鎖を絵画として定着させることだったのかもしれません。

コードで再現してみて思うのは、「アート」と「物理」の境界は意外と曖昧だということ。振り子に任せた結果が美しいなら、それは自然法則が美しいということなのかも。

色彩スキームを変えたり、パラメータをいじったりして遊んでみると楽しいかもしれません。

参考文献

Discussion