前言
我是一個後端工程師。白天寫 API、調資料庫、處理分散式系統的各種疑難雜症。但下了班之後,我會打開另一個世界——用程式碼畫出會動的幾何圖形、用雷射切割機刻出精密的摺紙結構、用數學公式生成從未存在過的花紋。
如果你看過《葬送的芙莉蓮》,應該記得那個場景——芙莉蓮用魔法讓一整片花田綻放,不是為了打仗,不是為了救人,純粹只是因為「很美」。
我把我這些跟工作無關的創作,叫做花田魔法。
這篇文章想聊聊一個後端工程師是怎麼走上創意編程(Creative Coding)這條路的,以及這段旅程如何回過頭來,讓我成為一個更好的工程師。
契機:一個無聊的週末
故事要從幾年前說起。那時候我剛入行不久,每天的工作就是 CRUD(Create, Read, Update, Delete),日復一日。技術上有在成長,但總覺得少了什麼——像是一直在解題,卻沒有在創造。
某個週末,我偶然在 YouTube 上看到 Daniel Shiffman 的 The Coding Train。他用 p5.js 在瀏覽器上畫出各種奇妙的視覺效果——碎形、粒子系統、流場⋯⋯ 整個人被電到了。
「原來程式碼可以做這種事?」
我當天晚上就裝好環境,跟著他的教學畫出了人生第一個粒子系統:
// 我的第一個 p5.js sketch
let particles = [];
function setup() {
createCanvas(800, 600);
background(20);
}
function draw() {
background(20, 10); // 半透明背景,產生拖尾效果
// 滑鼠按下時產生粒子
if (mouseIsPressed) {
for (let i = 0; i < 5; i++) {
particles.push(new Particle(mouseX, mouseY));
}
}
// 更新和繪製每個粒子
for (let i = particles.length - 1; i >= 0; i--) {
particles[i].update();
particles[i].display();
if (particles[i].isDead()) {
particles.splice(i, 1);
}
}
}
class Particle {
constructor(x, y) {
this.pos = createVector(x, y);
this.vel = p5.Vector.random2D().mult(random(1, 4));
this.acc = createVector(0, 0.05); // 重力
this.lifespan = 255;
this.size = random(3, 8);
}
update() {
this.vel.add(this.acc);
this.pos.add(this.vel);
this.lifespan -= 3;
}
display() {
noStroke();
fill(255, 100, 100, this.lifespan);
ellipse(this.pos.x, this.pos.y, this.size);
}
isDead() {
return this.lifespan <= 0;
}
}
那個晚上我玩到凌晨三點。不是因為趕 deadline,純粹是因為太好玩了。
從 p5.js 到 GLSL:效能的追求
p5.js 的好處是門檻極低——JavaScript + Canvas,瀏覽器打開就能跑。但當我想做更複雜的視覺效果時,很快就撞到效能瓶頸。幾千個粒子就開始掉幀了。
這時候我開始接觸 GLSL(OpenGL Shading Language)——直接在 GPU 上跑的程式語言。
GLSL 的思維方式跟一般程式完全不同。你不是在寫「對每個粒子做什麼」,而是在寫「對每個像素做什麼」。這是一種極度平行化的思考方式:
// Fragment Shader: 產生漣漪效果
precision mediump float;
uniform float u_time;
uniform vec2 u_resolution;
void main() {
// 把像素座標正規化到 -1 ~ 1
vec2 uv = (gl_FragCoord.xy * 2.0 - u_resolution) / min(u_resolution.x, u_resolution.y);
// 計算到中心的距離
float d = length(uv);
// 用距離和時間產生漣漪
float ripple = sin(d 20.0 - u_time 3.0) * 0.5 + 0.5;
// 隨距離衰減
ripple *= smoothstep(1.0, 0.0, d);
// 上色
vec3 color = mix(
vec3(0.1, 0.05, 0.2), // 深紫
vec3(1.0, 0.3, 0.5), // 粉紅
ripple
);
gl_FragColor = vec4(color, 1.0);
}
後端工程師的你可能會覺得這語法很陌生,但其實概念跟我們做資料處理時的 map 操作很像——對每個元素(像素)套用同一個函式,然後 GPU 幫你平行處理幾百萬個像素。
學 GLSL 的過程中,我最推薦的資源是 The Book of Shaders。它用互動式的方式教你 shader 的基礎,非常適合完全沒接觸過圖形程式的人。
數形結合:摺紙的數學之美
某天在逛 YouTube 的時候,我看到 Robby Kraft 用程式產生摺紙的摺痕圖案(Crease Pattern),然後真的把它摺出來。那個瞬間我意識到:數學和實體可以這樣結合!
摺紙不是小朋友的玩具。在數學上,摺紙涉及到:
- Huzita-Hatori 公理:定義了摺紙可以做到的幾何操作
- 川崎定理:一個頂點周圍的角度交替相加,和必須是 180 度
- 三浦摺疊(Miura-ori):可以用一個動作展開/摺疊的結構,NASA 用它來設計太陽能板
# 生成三浦摺疊的摺痕圖案
import numpy as np
import matplotlib.pyplot as plt
def miura_ori_pattern(rows, cols, angle_deg=75, cell_width=1.0, cell_height=1.0):
"""生成三浦摺疊的摺痕座標"""
angle = np.radians(angle_deg)
vertices = []
for j in range(rows + 1):
row = []
for i in range(cols + 1):
x = i * cell_width
if j % 2 == 1:
x += cell_width 0.3 np.cos(angle)
y = j cell_height np.sin(angle)
row.append((x, y))
vertices.append(row)
return vertices
# 畫出摺痕圖案
pattern = miura_ori_pattern(6, 8)
fig, ax = plt.subplots(figsize=(10, 6))
for j, row in enumerate(pattern):
xs, ys = zip(*row)
ax.plot(xs, ys, 'b-', linewidth=0.5)
for i in range(len(pattern[0])):
xs = [pattern[j][i][0] for j in range(len(pattern))]
ys = [pattern[j][i][1] for j in range(len(pattern))]
ax.plot(xs, ys, 'r-', linewidth=0.5)
ax.set_aspect('equal')
plt.title('Miura-ori Crease Pattern')
plt.savefig('miura_ori.svg')
我用程式產生摺痕圖案,然後用雷射雕刻機在紙上刻出壓痕線,就能精確地摺出複雜的結構。這個流程從數學公式 → 程式碼 → 雷射雕刻 → 實體摺紙,每一步都讓我興奮不已。
雷射雕刻:數位與實體的橋樑
說到雷射雕刻,這是我目前最常用來把數位創作帶到實體世界的工具。
我的工作流程大概是這樣:
- 用 p5.js 或 Python 生成向量圖案(SVG 格式)
- 在 LightBurn 軟體中調整雷射參數
- 用雷射切割機在木板、壓克力或紙上雕刻/切割
最有趣的是用參數化設計的方式來生成圖案。改幾個參數,就能產生完全不同的花紋:
// p5.js: 生成參數化的雷射切割圖案
function setup() {
createCanvas(800, 800, SVG); // 用 SVG 模式輸出向量圖
noLoop();
}
function draw() {
background(255);
translate(width/2, height/2);
stroke(0);
strokeWeight(0.1); // 雷射切割線要很細
noFill();
// 參數
const rings = 12;
const pointsPerRing = 60;
const noiseScale = 0.03;
const radiusStep = 25;
for (let r = 1; r <= rings; r++) {
beginShape();
for (let i = 0; i <= pointsPerRing; i++) {
const angle = map(i, 0, pointsPerRing, 0, TWO_PI);
const baseRadius = r * radiusStep;
const noiseVal = noise(
cos(angle) noiseScale r + 100,
sin(angle) noiseScale r + 100
);
const radius = baseRadius + noiseVal * 30;
const x = cos(angle) * radius;
const y = sin(angle) * radius;
vertex(x, y);
}
endShape(CLOSE);
}
save('laser_pattern.svg');
}
生成的 SVG 直接丟給雷射機,幾分鐘後一個獨一無二的木製杯墊就完成了。每次改參數都是新的圖案——這就是生成式藝術的魅力。
動力雕塑:會動的數學
最近我開始嘗試動力雕塑(Kinetic Sculpture)——會動的藝術品。靈感來自 Anthony Howe 的風力雕塑和 Theo Jansen 的海灘獸(Strandbeest)。
動力雕塑需要結合:
- 機構學:連桿、齒輪、凸輪的運動分析
- 3D 建模:用 OpenSCAD 或 Fusion 360 設計零件
- Arduino:控制馬達和感測器
- 數學:利薩茹曲線、擺線等週期運動
// Arduino: 控制兩軸動力雕塑的馬達
#include <Servo.h>
#include <math.h>
Servo servoA;
Servo servoB;
float timeStep = 0;
const float FREQ_A = 1.0; // 第一軸頻率
const float FREQ_B = 1.618; // 第二軸頻率(黃金比例,永不重複)
const float AMP = 45.0; // 擺動幅度(度)
void setup() {
servoA.attach(9);
servoB.attach(10);
}
void loop() {
// 利薩茹運動
float angleA = 90 + AMP sin(FREQ_A timeStep);
float angleB = 90 + AMP sin(FREQ_B timeStep);
servoA.write((int)angleA);
servoB.write((int)angleB);
timeStep += 0.02;
delay(20);
}
兩個頻率的比值是無理數(黃金比例),所以運動軌跡永遠不會重複,每一刻都是獨特的——這個概念讓我著迷。
花田魔法的意義
回到那個比喻。在《葬送的芙莉蓮》裡,芙莉蓮收集了各種「沒用的魔法」——讓花朵綻放的魔法、把銅像擦乾淨的魔法、讓衣服香香的魔法。這些魔法在戰鬥中毫無用處,但它們構成了芙莉蓮最珍貴的回憶。
我的花田魔法也是如此:
- p5.js 和 GLSL:讓螢幕上開出數學的花
- 摺紙數學:用公式折出實體的幾何之美
- 雷射雕刻:把程式碼變成摸得到的東西
- 動力雕塑:讓數學在空間中跳舞
這些東西不會幫我升職加薪。但它們讓我保持對程式設計的熱情,讓我在寫了一整天 CRUD 之後,還願意打開編輯器寫 code。
更重要的是,這些「沒用的技能」時不時會出乎意料地有用:
- GLSL 的平行化思維 → 幫助我理解 GPU 運算和資料平行處理
- 參數化設計的經驗 → 讓我在設計 API 時更注重靈活性
- 摺紙的幾何直覺 → 讓我在處理空間資料時更得心應手
- Arduino 的硬體經驗 → 讓我對 IoT 相關的後端設計更有感覺
給想開始的你
如果你也是後端工程師,想試試創意編程,我的建議是:
- 從 p5.js 開始:門檻最低,線上編輯器打開就能玩,社群超活躍
- 不要追求完美:創意編程的重點是探索和實驗,不是寫出最佳化的 code
- 找到自己的媒介:也許你喜歡的是 2D 圖案、3D 列印、互動裝置,或是音樂視覺化
- 分享你的作品:放到 Open Processing、Instagram、或自己的部落格(像我一樣)
- 享受「沒用」的快樂:不是所有東西都要有用。有些東西的價值就在於「美」本身
小結
後端工程師和創意編程者,看似是兩個不同的身份,但其實共享同一個核心能力——用程式解決問題。只是一個解決的是商業問題,一個解決的是美學問題。
我的花田魔法讓我的工程師生活不只是工作,而是一場持續的探索。如果你也想找到自己的花田魔法,現在就打開 p5.js Web Editor,畫一個圓開始吧。
延伸閱讀
- The Coding Train — Daniel Shiffman 的 p5.js 教學頻道
- The Book of Shaders — GLSL shader 互動教學
- Origami Simulator — Amanda Ghassaei 的摺紙模擬器
- The Nature of Code — 用程式模擬自然現象的經典教材