#02 ビジュアルコーディングを始めよう

ループでパターンを埋め尽くす

前回は1つの四角形を描きました。今回は for ループを使って「キャンバスを埋め尽くす」技法を学びます。

ビジュアルコーディングにおけるループは、単なる繰り返し処理ではありません。ループのカウンター変数 i位置・色・サイズを決める数学式の材料になります。これがコードからビジュアルが生まれる仕組みの核心です。


1次元ループ — 横方向に並べる

まず1次元のループから始めます。カウンター ix 座標として使います。

for (var i = 0; i < 10; i++) {
  var x = i * 60;                          // i番目のx位置
  ctx.fillStyle = 'hsl(' + (i * 36) + ', 80%, 55%)';  // i番目の色
  ctx.fillRect(x, 0, 58, H);
}

ここでの重要な考え方:

  • i * 60 → 等間隔にx座標を生成
  • i * 36 → 0〜360(10等分)の色相を生成
  • 58 は幅で、60 - 2 にすることで隙間ができる
1次元ループで並べたカラーバー
for (var i = 0; i < 10; i++) {
ctx.fillStyle = 'hsl(' + (i * 36) + ', 80%, 55%)';
ctx.fillRect(i * 60, 0, 58, H);
}

2次元ループ — グリッドを作る

ループを2重にネストすると、縦横のグリッドが作れます。

var size = 40;   // セルのサイズ

for (var x = 0; x < W; x += size) {        // 横方向
  for (var y = 0; y < H; y += size) {      // 縦方向
    ctx.fillStyle = 'hsl(' + ((x + y) / (W + H) * 360) + ', 70%, 55%)';
    ctx.fillRect(x, y, size - 2, size - 2);
  }
}

(x + y) / (W + H) * 360 という式が肝です。

  • x + y は左上から右下に向かって大きくなる値
  • それを W + H で割って 0〜1 に正規化
  • 360 を掛けて色相(0〜360)に変換

この「インデックス → 正規化 → 色相」の変換パターンは、ビジュアルコーディングで何度も登場します。

2次元ループのグリッド — 対角線方向にグラデーション
var size = 40;
for (var x = 0; x < W; x += size) {
for (var y = 0; y < H; y += size) {
  var hue = (x + y) / (W + H) * 360;
  ctx.fillStyle = 'hsl(' + hue + ', 70%, 55%)';
  ctx.fillRect(x, y, size - 2, size - 2);
}
}

隙間ゼロにする

size - 2size に変えると隙間がなくなり、ピクセルアート風になります。

隙間なしのタイルパターン
var size = 40;
for (var x = 0; x < W; x += size) {
for (var y = 0; y < H; y += size) {
  var hue = (x + y) / (W + H) * 360;
  ctx.fillStyle = 'hsl(' + hue + ', 70%, 55%)';
  ctx.fillRect(x, y, size, size);
}
}

数学関数で模様を変える

色相の計算式に Math.sin() を加えると、直線的なグラデーションではなく波状のパターンが生まれます。

var brightness = (Math.sin(x * 0.05) * Math.sin(y * 0.05) + 1) / 2;
// Math.sin の値域は -1 〜 1 なので、+1 して /2 すると 0〜1 に正規化
ctx.fillStyle = 'hsl(200, 80%, ' + (brightness * 60 + 20) + '%)';

Math.sin(x * 0.05) * Math.sin(y * 0.05) は、縦横それぞれの波を掛け合わせた干渉パターンです。

sinの干渉パターン — 波の掛け合わせ
var size = 20;
for (var x = 0; x < W; x += size) {
for (var y = 0; y < H; y += size) {
  var v = (Math.sin(x * 0.06) * Math.sin(y * 0.06) + 1) / 2;
  ctx.fillStyle = 'hsl(200, 80%, ' + (v * 60 + 15) + '%)';
  ctx.fillRect(x, y, size, size);
}
}

すべてのピクセルを制御する

サイズを 1 にすると、1ピクセルずつすべてを塗れます。

for (var x = 0; x < W; x++) {
  for (var y = 0; y < H; y++) {
    var hue = (x * x + y * y) % 360;     // 距離の二乗から色相を計算
    ctx.fillStyle = 'hsl(' + hue + ', 90%, 50%)';
    ctx.fillRect(x, y, 1, 1);
  }
}

x * x + y * y は原点からの距離の二乗(ピタゴラスの定理)。これを使うと中心から同心円状に色が広がります。

全ピクセル制御 — 距離の二乗で同心円パターン
ctx.fillStyle = '#0d1117';
ctx.fillRect(0, 0, W, H);
for (var x = 0; x < W; x++) {
for (var y = 0; y < H; y++) {
  var hue = ((x - W/2) * (x - W/2) + (y - H/2) * (y - H/2)) * 0.003 % 360;
  ctx.fillStyle = 'hsl(' + hue + ', 90%, 50%)';
  ctx.fillRect(x, y, 1, 1);
}
}

ピクセル単位のループはキャンバスが大きいと少し描画に時間がかかります。サイズを 24 にして間引くと高速化できます。


ランダム性を加える

Math.random() は 0〜1 のランダムな数値を返します。位置や色に掛け合わせると、散らばったパターンが生まれます。

for (var i = 0; i < 500; i++) {
  var x = Math.random() * W;
  var y = Math.random() * H;
  ctx.fillStyle = 'hsl(' + (Math.random() * 360) + ', 80%, 60%)';
  ctx.fillRect(x, y, 4, 4);
}
ランダム散布 — Math.random() で位置と色をバラバラに
ctx.fillStyle = '#0d1117';
ctx.fillRect(0, 0, W, H);
for (var i = 0; i < 800; i++) {
var x = Math.random() * W;
var y = Math.random() * H;
ctx.fillStyle = 'hsl(' + (Math.random() * 360) + ', 80%, 60%)';
ctx.fillRect(x, y, 4, 4);
}

ループのパターンまとめ

ビジュアルコーディングで使う典型的な計算パターンをまとめます。

やりたいこと式の例
等間隔の位置i * spacing
全体を0〜1にi / count
0〜360の色相(i / count) * 360
-1〜1の波Math.sin(i * freq)
ランダム値Math.random()
0〜360のランダムMath.random() * 360

これらを組み合わせることで、コードの数行が数万のピクセルを支配するのがビジュアルコーディングの魔法です。


まとめ

この回でやったこと:

  • 1次元ループで横に並べた
  • 2重ループで 座標系? Canvas上の位置を(x, y)の数値ペアで表す仕組み。左上が原点(0,0)でxは右向き、yは下向きに増加する(数学の座標系とy軸が逆)。 グリッドを作った
  • ループのインデックスを HSL? 色相(Hue 0〜360)・彩度(Saturation)・明度(Lightness)で色を指定する方式。ループ変数から色相を計算しやすく、ビジュアルコーディングで多用される。 の色相に変換した
  • Math.sin() で波状パターンを作った
  • Math.random() でランダムな散布図を作った

次回は 三角関数 に入ります。Math.sin()Math.cos() を使って、ローズ曲線やスパイラルといった滑らかで有機的な曲線を描いていきます。