前言
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 是一個很棒的創意編程遊樂場。它降低了入門門檻,讓你專注在最重要的事情上——探索和創造。
我在這個平台上從一個完全不懂圖形程式的後端工程師,漸漸變成一個能用程式碼表達想法的創作者。這段旅程中最大的收穫,不是任何特定的技術,而是一種看待程式碼的新方式——它不只是工具,也可以是畫筆。
延伸閱讀
- Open Processing — 創意編程社群平台
- p5.js Reference — p5.js 官方文件
- Generative Design — 生成式設計教學書
- Creative Coding Weekly — 每週創意編程精選
- Tim Holman 的 Generative Artistry — 手把手教你做生成式藝術