【p5.js 作例】「こうしたい」を捨てる創造:予測不能な数式が描くパターン

2025年12月11日木曜日

p5.js 作例

t f B! P L
title image

特定の意味や目的を持たない数式を組み合わせ、それを基に形を描くことに意味はあるでしょうか?

わかりません。でも楽しいんです、これが。 😊

特に意味のない数式から、自分では想像できなかったような形が描画される驚きと興奮! 今回、p5.js を使ったコードで、この楽しさをご紹介します。

You can read this article in English.



あらためまして、この記事は「Processing Advent Calendar 2025」11日目の記事です。
昨日はセンバク(@senbaku)さんによる、クリエイティブ・コーディングのお題を配布する Web1.0 テイストの手作りサイトのご紹介でした。

創作のネタに詰まったときに、とっても役立ちそうですね!😉✨


x、y座標を数式で算出して描画

ある数式で x、y座標を算出して、そこに点を打っていく。そんなコードを考えてみましょう。

例えば「円」がよい例です。x、y座標を以下の数式で計算すると、その描画結果は円になります。


let x = r * cos(t);
let y = r * sin(t);



この x、y座標を計算する数式をいろいろ変えてみると面白いことになるんじゃないか?というお話しです。

 

数式を変えてみる

例えば、先程の円を描く数式をこう変えてみると…


let x = r * cos(t);
let y = r * sin(t * 2);


Lissajous curve

いわゆるリサジュー図形になりますね。

リサジュー図形: Wikipedia

係数の比率(例:3:2 や 3:5)を変えるだけで、異なるパターンが生まれます。


let x = r * cos(t * 3);
let y = r * sin(t * 2);


Lissajous curve


let x = r * cos(t * 3);
let y = r * sin(t * 5);


Lissajous curve

 

「こうしたい」を捨てて、予測不能を愉しむ

描画をキャンバス内に収めるには、ある範囲内の数値を返す数式が望ましいです。 sin() や cos() は -1 から 1 の間の値を返してくれるので、この用途にうってつけです。

Sine and cosine graphs

-1 から 1 の間の値を返す数式はいろいろ考えられます。もっと複雑な数式に、例えばこうするとどうなるでしょう?


let x = r * cos(PI * (sin(PI * t) + 0.5));
let y = r * sin(PI * (t + 0.5)) * cos(PI * t * 0.5);


唐突にややこしい数式が出てきましたが、ここはブラックボックスとして「なんか式が長くなったなー」程度に一旦受け入れてください。

この描画結果は以下になります。

これは、結果を先に想像して、そうなるように数式を考えるわけではありません。結果がどうなるかは全く考えずに、-1 から 1 の範囲を返す数式ということだけを考えるのです。

すると、自分で書いたコードでありながら、想像を超える結果に自分でビックリする、という楽しさが味わえます。(想像以上に面白くない結果ということも、ままありますが…)

 

今回の作例コード

このコードでは、数式から半径と角度を算出して、それを元にした極座標として x、y座標を計算しています。

さらに、変化するパラメータを 2つ(pA と pB)に増やして、ここまで 1本の線だった図を「複数の線が少しずつ形を変えながら描かれる」ように、描画を 2重ループで制御しました。

数式に与えるパラメータ値はランダムにして、実行のたびにランダムな結果が描画されるようにしています。


/*
 * p5.js サンプルコード
 * 意味のない数式で、意味のない形を描く
 * 
 * @author @deconbatch
 * @version 0.1
 * @license CC0 1.0 https://creativecommons.org/publicdomain/zero/1.0/deed.ja
 * p5js 2.0.0
 * created 2025.12.08
 */

function setup() {

  const CANVAS_SIZE = 640;

  // 曲線の形を決定するパラメータ。ランダムに初期値を決定。
  const A_FROM = random(-1, 1);
  const B_FROM = random(-1, 1);
  const A_TO   = A_FROM + random(0.3, 0.5);
  const B_TO   = B_FROM + random(0.3, 0.5);
  const A_STEP = 0.005;
  const B_STEP = 0.1;

  createCanvas(CANVAS_SIZE, CANVAS_SIZE);
  noFill();
  stroke("MidnightBlue");
  strokeWeight(1);
  
  background("CornSilk");
  translate(width * 0.5, height * 0.5);

  // 曲線の密度、本数に関わるループ
  for (let pA = A_FROM; pA < A_TO; pA += A_STEP) {
    beginShape();
    // 曲線の滑らかさに関わるループ
    for (let pB = B_FROM; pB < B_TO; pB += B_STEP) {
      let r = CANVAS_SIZE * 0.5 * calcRadiusFactor(pA, pB);
      let t = PI * calcAngleFactor(pA, pB);
      let x = r * cos(t);
      let y = r * sin(t);
      vertex(x, y);
    }
    endShape();
  }

}

/**
 * 半径の「変動率」を決定する、意味のない数式。
 * 返り値は-1から1の範囲。これを半径にかけてキャンバスに収める。
 */
function calcRadiusFactor(_pA, _pB) {
  return cos(PI * (sin(TWO_PI * _pA * _pB) + _pB));
}

/**
 * 角度を決定する、意味のない数式。
 * 返り値は-1から1の範囲。これをPIにかけることで、角度(-PIからPI)として利用する。
 */
function calcAngleFactor(_pA, _pB) {
  return sin(PI * (_pA + _pB)) * cos(PI * _pA * _pB);
}



Unpredictable mathematical patterns

Unpredictable mathematical patterns

この手法を煎じ詰めて、さらに改良・拡張することで、一例として以下のような表現を実現できました。

 

まとめ

今回のコードには、何が出来るかわからないという面白さがあります。これもまたクリエイティブ・コーディングの醍醐味ですね。

「こういうものを作りたい、だからこういうコードを書く」ではなく、「こういうコードを書いたらどんなものが出来るんだろう?」これが許されるのがクリエイティブ・コーディングだと思います。


終わりに

今回のコードを元にアニメーションにしたものを Pattlas に上げてみました。よろしければ合わせてご覧ください。

意味のない数式で、意味のない形を描くアニメーション。

さて、明日の「Processing Advent Calendar 2025」は、dot notさんによる 12日目の記事です。お楽しみに! 😘💕

 

QooQ