前言

我是一個後端工程師。白天寫 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')

我用程式產生摺痕圖案,然後用雷射雕刻機在紙上刻出壓痕線,就能精確地摺出複雜的結構。這個流程從數學公式 → 程式碼 → 雷射雕刻 → 實體摺紙,每一步都讓我興奮不已。


雷射雕刻:數位與實體的橋樑

說到雷射雕刻,這是我目前最常用來把數位創作帶到實體世界的工具。

我的工作流程大概是這樣:

  1. 用 p5.js 或 Python 生成向量圖案(SVG 格式)
  2. 在 LightBurn 軟體中調整雷射參數
  3. 用雷射切割機在木板、壓克力或紙上雕刻/切割

最有趣的是用參數化設計的方式來生成圖案。改幾個參數,就能產生完全不同的花紋:

// 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 相關的後端設計更有感覺

給想開始的你

如果你也是後端工程師,想試試創意編程,我的建議是:

  1. 從 p5.js 開始:門檻最低,線上編輯器打開就能玩,社群超活躍
  2. 不要追求完美:創意編程的重點是探索和實驗,不是寫出最佳化的 code
  3. 找到自己的媒介:也許你喜歡的是 2D 圖案、3D 列印、互動裝置,或是音樂視覺化
  4. 分享你的作品:放到 Open Processing、Instagram、或自己的部落格(像我一樣)
  5. 享受「沒用」的快樂:不是所有東西都要有用。有些東西的價值就在於「美」本身

小結

後端工程師和創意編程者,看似是兩個不同的身份,但其實共享同一個核心能力——用程式解決問題。只是一個解決的是商業問題,一個解決的是美學問題。

我的花田魔法讓我的工程師生活不只是工作,而是一場持續的探索。如果你也想找到自己的花田魔法,現在就打開 p5.js Web Editor,畫一個圓開始吧。

延伸閱讀