前言

Open Processing 是我最喜歡的創意編程平台之一。它就像是程式藝術界的 CodePen——你可以在瀏覽器上直接寫 p5.js,即時看到結果,還能分享給全世界的人。

這篇文章想跟大家分享我在 Open Processing 上的幾個作品、背後的創作思路,以及這段旅程中學到的東西。如果你也想踏入創意編程的世界,希望這篇文章能給你一些靈感和方向。


Open Processing 是什麼?

Open Processing 是一個線上的創意編程社群平台,主要支援 Processing 和 p5.js。它的特色包括:

  • 線上編輯器:不需要安裝任何東西,打開瀏覽器就能寫 code
  • 即時預覽:修改程式碼立即看到視覺效果
  • 社群互動:可以看別人的作品、fork 來修改、留言交流
  • 每日挑戰:定期有主題創作活動
  • Curated Collections:編輯精選的作品集合

跟其他類似平台(如 Shadertoy、CodePen)相比,Open Processing 的社群特別友善,而且 p5.js 的上手難度比 GLSL 低很多,非常適合入門。

帳號建立與第一個 Sketch

註冊帳號後,點擊「Create a Sketch」就能開始:

// Open Processing 上的 Hello World
function setup() {
  createCanvas(400, 400);
}

function draw() { background(220); circle(mouseX, mouseY, 50); }

就這麼簡單,一個跟著滑鼠移動的圓。但別小看這個起點——所有複雜的作品都是從這裡開始的。


作品分享(一):Flow Field — 流場粒子

概念

Flow Field(流場)是創意編程中非常經典的技法。概念是:用 Perlin noise 在畫布上建立一個「風場」,然後讓粒子隨著這個場流動。

核心程式碼

const PARTICLE_COUNT = 2000;
const NOISE_SCALE = 0.005;
const SPEED = 2;

let particles = []; let flowField; let cols, rows; let cellSize = 10;

function setup() { createCanvas(800, 600); background(15, 15, 25);

cols = floor(width / cellSize); rows = floor(height / cellSize); flowField = new Array(cols * rows);

for (let i = 0; i < PARTICLE_COUNT; i++) { particles.push({ pos: createVector(random(width), random(height)), vel: createVector(0, 0), prevPos: createVector(0, 0), color: color( random(150, 255), random(50, 150), random(100, 200), 15 // 低透明度,疊加出層次感 ) }); } }

function draw() { // 不清除背景,讓軌跡累積

// 更新流場 let yOff = frameCount * 0.001; // 時間維度的 noise for (let y = 0; y < rows; y++) { for (let x = 0; x < cols; x++) { let angle = noise(x NOISE_SCALE, y NOISE_SCALE, yOff) TWO_PI 2; flowField[x + y * cols] = p5.Vector.fromAngle(angle); } }

// 更新粒子 for (let p of particles) { p.prevPos.set(p.pos);

let col = floor(p.pos.x / cellSize); let row = floor(p.pos.y / cellSize); let index = constrain(col + row * cols, 0, flowField.length - 1);

let force = flowField[index]; p.vel.add(force); p.vel.limit(SPEED); p.pos.add(p.vel);

// 邊界處理:循環 if (p.pos.x > width) p.pos.x = 0; if (p.pos.x < 0) p.pos.x = width; if (p.pos.y > height) p.pos.y = 0; if (p.pos.y < 0) p.pos.y = height;

// 畫線 stroke(p.color); strokeWeight(1); line(p.prevPos.x, p.prevPos.y, p.pos.x, p.pos.y); } }

創作心得

這個作品教會我一件事:簡單的規則可以產生複雜的美。每個粒子只是跟著 noise 場移動,但數千個粒子的軌跡疊加在一起,就形成了像河流、像風、像生命一樣的圖案。

做這個作品時我花最多時間的不是程式邏輯,而是調參數——noise 的 scale、粒子的速度、顏色的透明度、是否清除背景⋯⋯ 每個參數微調都會帶來截然不同的感覺。


作品分享(二):Recursive Tree — 遞迴樹

概念

遞迴是程式設計的基本功,也是碎形藝術的基礎。這個作品用遞迴畫出一棵隨風搖擺的樹。

let windAngle = 0;

function setup() { createCanvas(600, 600); }

function draw() { background(20, 25, 30);

// 風的效果 windAngle = sin(frameCount 0.02) 0.1;

translate(width / 2, height); stroke(200, 180, 150);

drawBranch(120, 0); }

function drawBranch(len, depth) { if (len < 4 || depth > 12) return;

// 隨深度改變顏色 let inter = map(depth, 0, 10, 0, 1); let c = lerpColor( color(100, 80, 60), // 樹幹色 color(80, 180, 100), // 葉子色 inter ); stroke(c); strokeWeight(map(len, 4, 120, 1, 8));

// 畫樹枝 line(0, 0, 0, -len); translate(0, -len);

// 加入風的擺動,越高處擺動越大 let wind = windAngle (depth 0.3);

// 左分支 push(); rotate(-PI / 6 + wind + random(-0.05, 0.05)); drawBranch(len * 0.72, depth + 1); pop();

// 右分支 push(); rotate(PI / 5 + wind + random(-0.05, 0.05)); drawBranch(len * 0.68, depth + 1); pop();

// 偶爾多一個分支 if (random() > 0.6 && depth > 2) { push(); rotate(random(-PI/4, PI/4) + wind); drawBranch(len * 0.5, depth + 1); pop(); } }

創作心得

遞迴樹是我最早完成的作品之一,也是我覺得最能展現「程式之美」的題材。因為它直覺地展示了自相似性——樹的每一個分支,本身就是一棵小樹。

關鍵的技巧是加入隨機性。完全對稱的遞迴樹很無聊,加一點 random 抖動、偶爾多一個分支、左右比例不同,就會變得像真正的樹一樣自然。


作品分享(三):Circle Packing — 圓填充

概念

Circle Packing 是一個經典的生成式藝術技法:在一個空間中盡可能填入不重疊的圓。

let circles = [];
const MAX_ATTEMPTS = 500;

function setup() { createCanvas(600, 600); background(15); noLoop(); }

function draw() { // 嘗試放置圓 for (let i = 0; i < 3000; i++) { addCircle(); }

// 繪製所有圓 for (let c of circles) { drawStyledCircle(c); } }

function addCircle() { let x = random(width); let y = random(height);

// 計算最大可能半徑 let maxR = 100; for (let other of circles) { let d = dist(x, y, other.x, other.y) - other.r; if (d < maxR) maxR = d; }

// 也要考慮邊界 maxR = min(maxR, x, y, width - x, height - y);

if (maxR > 2) { // 最小半徑門檻 circles.push({ x: x, y: y, r: maxR, hue: map(maxR, 2, 100, 200, 350) % 360 }); } }

function drawStyledCircle(c) { // 大圓填色,小圓只有輪廓 if (c.r > 10) { let col = color(hsla(${c.hue}, 70%, 60%, 0.3)); fill(col); stroke(255, 40); strokeWeight(0.5); } else { noFill(); stroke(255, 20); strokeWeight(0.5); } circle(c.x, c.y, c.r * 2);

// 大圓內加裝飾 if (c.r > 20) { noFill(); stroke(255, 15); for (let i = 1; i < 4; i++) { circle(c.x, c.y, c.r 2 (i / 4)); } } }

創作心得

Circle Packing 是演算法和美學結合的好例子。核心演算法很簡單(隨機放圓、檢查碰撞),但視覺效果非常豐富。

我學到的一課是:裝飾(decoration)很重要。單純的填充圓看起來還好,但加上同心圓裝飾、顏色映射、透明度變化,整個作品的質感就上了一個層次。


作品分享(四):波紋干涉 — Wave Interference

概念

物理學中的波紋干涉,用程式可以美麗地呈現:

let sources = [];

function setup() { createCanvas(600, 600, WEBGL); // 產生幾個波源 sources.push({ x: -100, y: -80 }); sources.push({ x: 120, y: 60 }); sources.push({ x: -50, y: 130 }); }

function draw() { background(0); noStroke();

let t = frameCount * 0.05;

// 用滑鼠控制一個波源 sources[0].x = mouseX - width/2; sources[0].y = mouseY - height/2;

let step = 6; for (let x = -width/2; x < width/2; x += step) { for (let y = -height/2; y < height/2; y += step) { // 計算所有波源的疊加 let sum = 0; for (let s of sources) { let d = dist(x, y, s.x, s.y); sum += sin(d 0.05 - t) / (1 + d 0.01); }

// 映射到顏色 let brightness = map(sum, -2, 2, 0, 255); let r = map(sum, -2, 2, 30, 255); let g = map(sum, -2, 2, 50, 150); let b = map(sum, -2, 2, 100, 50);

fill(r, g, b); rect(x, y, step, step); } } }

創作心得

波紋干涉讓我重新感受到物理的美。兩個簡單的正弦波疊加,就能產生複雜的干涉圖案。加上互動(滑鼠控制波源位置),觀眾可以「玩」這個作品,而不只是看。


創作流程與方法論

經過這些作品的累積,我慢慢發展出自己的創作流程:

1. 靈感收集

  • 逛 Open Processing 的 Trending 和 Curated 頁面
  • 追蹤 Instagram 上的 #creativecoding 和 #generativeart
  • 看數學和物理的科普影片,把概念視覺化
  • 日常觀察:自然界的紋理、建築的幾何、光影的變化

2. 快速原型

用最少的 code 把核心概念實現出來。不管好不好看,先讓它動起來:

// 原型階段:focus on concept, not aesthetics
function setup() {
  createCanvas(400, 400);
}
function draw() {
  background(0);
  // 用最簡單的方式驗證想法
  for (let i = 0; i < 50; i++) {
    let angle = i  0.2 + frameCount  0.01;
    let r = i * 3;
    circle(200 + cos(angle)  r, 200 + sin(angle)  r, 5);
  }
}

3. 迭代精煉

原型確認可行後,開始加入:

  • 顏色設計(通常用 HSB 色彩空間比較好控制)
  • 動態效果(時間變化、互動)
  • 細節裝飾(線條、紋理、粒子)
  • 效能優化(減少不必要的計算)

4. 最終調整

  • 解析度和畫面比例
  • 錄製 GIF 或影片(用 CCapture.js)
  • 寫作品說明

給新手的建議

從模仿開始,沒關係的

看到喜歡的作品,fork 它、改它、理解它。每個創作者都是從模仿開始的。重點不是原創,而是在模仿的過程中學會技巧和培養品味。

不要急著做大作品

一個好看的小 sketch(50 行以內)比一個半成品的大專案更有價值。完成比完美重要。

享受「happy accidents」

創意編程最棒的時刻,往往是程式寫錯的時候。某個參數多了一個零、某個公式打錯了,結果卻意外地美——這時候不要急著修 bug,先觀察它、理解它、保存它。

建立自己的工具箱

把常用的函式整理成模組:noise wrapper、色彩工具、粒子系統、幾何運算⋯⋯ 下次創作就能更快進入狀態。


小結

Open Processing 是一個很棒的創意編程遊樂場。它降低了入門門檻,讓你專注在最重要的事情上——探索和創造。

我在這個平台上從一個完全不懂圖形程式的後端工程師,漸漸變成一個能用程式碼表達想法的創作者。這段旅程中最大的收穫,不是任何特定的技術,而是一種看待程式碼的新方式——它不只是工具,也可以是畫筆。

延伸閱讀