前言

如果你有在做創意編程,應該或多或少聽過 NFT(Non-Fungible Token)和生成式藝術(Generative Art)的交集。從 2021 年 Art Blocks 平台上 Tyler Hobbs 的 Fidenza 系列賣出天價開始,「用程式碼生成的藝術品」突然變成了一個有市場價值的東西。

我自己雖然不是靠 NFT 賺錢的人,但作為一個做 p5.js 創作的後端工程師,我對這個領域做了不少研究,也嘗試過把作品放上鏈。這篇文章想客觀地分享我對生成式藝術 NFT 的理解——平台介紹、技術門檻、市場現況,以及我個人的看法。

提醒:這篇文章不是投資建議。NFT 市場波動極大,請自行評估風險。


什麼是生成式藝術 NFT?

傳統的 NFT 藝術品通常是一張圖片或一段影片——藝術家創作完成後,把成品放到鏈上。

生成式藝術 NFT 不一樣。藝術家上傳的是程式碼,每次有人鑄造(mint)的時候,程式碼會根據隨機種子生成一個獨特的作品

也就是說:

  • 藝術家寫的是「規則」(演算法)
  • 每個買家拿到的是規則的一個「實例」
  • 同一套規則可以產生幾十到幾千個不同的作品
  • 每個作品都是獨一無二的
// 概念示意:同一套規則,不同的種子
function setup() {
  // 隨機種子決定了這次的作品長什麼樣
  randomSeed(tokenData.hash);
  noiseSeed(tokenData.hash);

createCanvas(800, 800); background(20);

// 同樣的演算法,不同的種子 → 不同的結果 let palette = generatePalette(); let composition = generateComposition();

render(palette, composition); }


主要平台介紹

Art Blocks

Art Blocks 是生成式藝術 NFT 的先驅和標竿。

特色:

  • 建在 Ethereum 上
  • 程式碼完全存在鏈上(on-chain)
  • 有嚴格的審核制度(Curated / Playground / Factory)
  • 作品品質普遍很高

技術要求:

  • 必須用純 JavaScript(vanilla JS)
  • 不能引用外部函式庫(所有依賴要打包進去)
  • 程式碼大小有限制(~10-20KB 壓縮後)
  • 必須是確定性的(deterministic):同一個 seed 永遠產生同一個結果
// Art Blocks 的標準接口
// tokenData.hash 是一個 66 字元的 hex string
// 你的程式碼必須用這個 hash 作為唯一的隨機來源

let tokenData = { hash: "0x..." // 由平台在 mint 時提供 };

// 把 hash 轉成可用的隨機數 function hashToRandom(hash) { let seed = parseInt(hash.slice(2, 10), 16); // 用 seed 初始化你的 PRNG return new SeededRandom(seed); }

// 自己寫或引入 PRNG(因為不能用 Math.random) class SeededRandom { constructor(seed) { this.seed = seed; }

// 簡單的 LCG(Linear Congruential Generator) next() { this.seed = (this.seed * 1664525 + 1013904223) & 0xFFFFFFFF; return (this.seed >>> 0) / 0xFFFFFFFF; }

// 範圍內的隨機數 range(min, max) { return min + this.next() * (max - min); }

// 隨機選擇 pick(array) { return array[Math.floor(this.next() * array.length)]; } }

fxhash

fxhash 是建在 Tezos 區塊鏈上的生成式藝術平台。

特色:

  • Tezos 的交易費用比 Ethereum 低很多(幾毛錢 vs 幾十美金)
  • 開放式平台,任何人都可以上傳作品
  • 社群非常活躍,有各種主題活動
  • 入門門檻低,適合新手

技術要求:

  • 支援 p5.js、Three.js 等外部函式庫
  • 使用 fxhash 變數作為隨機種子
  • 需要呼叫 fxpreview() 產生預覽圖
// fxhash 的標準模板
// fxhash 和 fxrand() 由平台注入

function setup() { createCanvas(800, 800); noLoop(); }

function draw() { background(20);

// 使用 fxrand() 而不是 random() // fxrand() 是確定性的,基於 fxhash 種子 let numCircles = Math.floor(fxrand() * 50) + 20;

for (let i = 0; i < numCircles; i++) { let x = fxrand() * width; let y = fxrand() * height; let r = fxrand() * 100 + 10; let hue = fxrand() * 360;

fill(hsla(${hue}, 70%, 60%, 0.5)); noStroke(); circle(x, y, r); }

// 告訴平台可以截圖了 fxpreview(); }

其他平台

  • Prohibition:以太坊上的策展型平台
  • Highlight:支援多鏈,比較新的平台
  • Verse:由 Art Blocks 團隊推出的二級市場

如何讓作品具有隨機種子

生成式藝術 NFT 的核心挑戰是:確定性隨機(Deterministic Randomness)

為什麼不能用 Math.random()

因為 Math.random() 每次執行結果都不同。但 NFT 需要:

  • 同一個 token 每次渲染都要產生完全一樣的結果
  • 不同 token 之間要有足夠的差異

自己實作 PRNG

// Mulberry32: 一個簡單但品質不錯的 32-bit PRNG
function mulberry32(a) {
  return function() {
    a |= 0;
    a = a + 0x6D2B79F5 | 0;
    var t = Math.imul(a ^ a >>> 15, 1 | a);
    t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
    return ((t ^ t >>> 14) >>> 0) / 4294967296;
  };
}

// 從 hash 字串產生種子 function hashToSeed(hash) { let seed = 0; for (let i = 0; i < hash.length; i++) { seed = ((seed << 5) - seed + hash.charCodeAt(i)) | 0; } return seed; }

// 使用 const rand = mulberry32(hashToSeed(tokenData.hash));

// 替代 p5.js 的 random() function myRandom(min, max) { if (max === undefined) { max = min; min = 0; } return min + rand() * (max - min); }

特徵(Features)的設計

好的生成式 NFT 會定義「特徵」——根據隨機種子決定的屬性。這些特徵會影響稀有度和價值:

// 定義作品的特徵
function generateFeatures(rand) {
  // 色彩方案:有些比其他的稀有
  const palettes = [
    { name: "Sunset", colors: ["#FF6B6B", "#FFE66D", "#4ECDC4"], weight: 30 },
    { name: "Ocean",  colors: ["#0077B6", "#00B4D8", "#90E0EF"], weight: 30 },
    { name: "Forest", colors: ["#2D6A4F", "#40916C", "#95D5B2"], weight: 25 },
    { name: "Neon",   colors: ["#FF006E", "#8338EC", "#3A86FF"], weight: 10 },
    { name: "Mono",   colors: ["#F8F9FA", "#ADB5BD", "#212529"], weight: 5 },
  ];

const palette = weightedPick(palettes, rand);

// 密度 const density = rand() < 0.7 ? "Normal" : "Dense";

// 構圖 const compositions = ["Radial", "Grid", "Organic", "Spiral"]; const composition = compositions[Math.floor(rand() * compositions.length)];

return { palette: palette.name, density: density, composition: composition, }; }

function weightedPick(items, rand) { const totalWeight = items.reduce((sum, item) => sum + item.weight, 0); let r = rand() * totalWeight; for (let item of items) { r -= item.weight; if (r <= 0) return item; } return items[items.length - 1]; }

在 fxhash 上,你可以用 $fxhashFeatures 來宣告特徵:

window.$fxhashFeatures = {
  "Palette": features.palette,
  "Density": features.density,
  "Composition": features.composition,
};

智能合約基礎

對後端工程師來說,智能合約其實就是「跑在區塊鏈上的程式」。如果你用 Art Blocks 或 fxhash,你不需要自己寫合約——平台會幫你處理。

但了解基本概念有助於理解整個系統:

ERC-721:NFT 的標準

// 簡化的 ERC-721 概念(Solidity 語言)
interface IERC721 {
    // 查詢擁有者
    function ownerOf(uint256 tokenId) external view returns (address);

// 轉移 NFT function transferFrom(address from, address to, uint256 tokenId) external;

// 查詢餘額 function balanceOf(address owner) external view returns (uint256); }

生成式 NFT 的合約邏輯

// 概念示意(簡化版)
contract GenerativeArt is ERC721 {
    mapping(uint256 => bytes32) public tokenIdToHash;
    uint256 public totalSupply;
    uint256 public maxSupply;
    string public script; // 藝術家的 JavaScript 程式碼

function mint() external payable { require(totalSupply < maxSupply, "Sold out");

uint256 tokenId = totalSupply; // 用區塊資訊和 tokenId 生成唯一的 hash bytes32 hash = keccak256( abi.encodePacked(blockhash(block.number - 1), tokenId, msg.sender) );

tokenIdToHash[tokenId] = hash; _mint(msg.sender, tokenId); totalSupply++; } }

這個 hash 就是你的 p5.js 程式碼收到的 tokenData.hash


市場現況與個人觀察

2021-2022:狂熱期

  • Art Blocks 的 FidenzaRingersChromie Squiggle 等系列賣出數百 ETH
  • 大量創作者和投機者湧入
  • fxhash 的 Tezos 生態蓬勃發展

2023-2024:冷卻期

  • 整體 NFT 市場大幅下滑
  • 投機者離開,但核心收藏家和創作者留下
  • 品質導向,策展更嚴格
  • 「生成式藝術」作為藝術形式的認同度提高(進入美術館和拍賣行)

2025-2026:現在

市場回歸到比較理性的狀態。一些觀察:

  • 好作品依然有市場,但「隨便做一個就能賣」的時代已經過去了
  • 社群和聲譽很重要:收藏家更看重藝術家的持續投入和社群參與
  • 跨媒介創作受歡迎:結合實體(版畫、裝置)的作品更有吸引力
  • 開源和教育被重視:分享過程和技術的創作者更受歡迎

我的個人看法

生成式藝術 NFT 對創意編程者來說,是一個有趣但不可靠的收入來源

我建議的心態是:

  1. 先做好作品,再考慮上鏈:不要為了 NFT 而做作品,那樣通常做不出好東西
  2. 當成展覽機會:把平台當成線上藝廊,而不是賺錢機器
  3. 投入社群:生成式藝術社群是全世界最友善的技術社群之一
  4. 控制成本:用 fxhash(Tezos)開始,Gas fee 便宜,風險低

實作:把一個 p5.js 作品準備成 fxhash 格式

以下是一個完整的範例,展示如何把普通的 p5.js sketch 改成 fxhash 相容的格式:

原始版本

// 普通的 p5.js sketch
function setup() {
  createCanvas(800, 800);
  noLoop();
}

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

for (let i = 0; i < 100; i++) { let x = random(width); let y = random(height); let r = random(10, 60); fill(random(255), random(255), random(255), 150); circle(x, y, r); } }

fxhash 版本

<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <script src="https://cdn.jsdelivr.net/npm/p5@1.9.0/lib/p5.min.js"></script>
  <script src="fxhash-snippet.js"></script>
  <style>
    body { margin: 0; overflow: hidden; background: #000; }
    canvas { display: block; }
  </style>
</head>
<body>
  <script src="sketch.js"></script>
</body>
</html>
// sketch.js — fxhash 版本
// fxrand() 和 fxhash 由 fxhash-snippet.js 提供

// 自訂的 random 函式,包裝 fxrand function fxRandom(min, max) { if (min === undefined) return fxrand(); if (max === undefined) { max = min; min = 0; } return min + fxrand() * (max - min); }

function fxRandomInt(min, max) { return Math.floor(fxRandom(min, max)); }

// 定義特徵 const PALETTES = [ { name: "Warm", colors: ["#FF6B6B", "#FFE66D", "#F7934C"] }, { name: "Cool", colors: ["#0077B6", "#00B4D8", "#48CAE4"] }, { name: "Earth", colors: ["#606C38", "#283618", "#DDA15E"] }, { name: "Neon", colors: ["#FF006E", "#8338EC", "#3A86FF"] }, ];

const palette = PALETTES[fxRandomInt(0, PALETTES.length)]; const count = fxRandomInt(50, 200); const hasGrid = fxrand() < 0.3;

// 設定 fxhash 特徵 window.$fxhashFeatures = { "Palette": palette.name, "Element Count": count < 100 ? "Sparse" : "Dense", "Has Grid": hasGrid ? "Yes" : "No", };

function setup() { let size = min(windowWidth, windowHeight); createCanvas(size, size); pixelDensity(2); noLoop(); }

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

if (hasGrid) { drawGrid(); }

for (let i = 0; i < count; i++) { let x = fxRandom(0, width); let y = fxRandom(0, height); let r = fxRandom(10, 60); let colorIdx = fxRandomInt(0, palette.colors.length);

let c = color(palette.colors[colorIdx]); c.setAlpha(150); fill(c); circle(x, y, r); }

// 告訴 fxhash 可以截圖了 fxpreview(); }

function drawGrid() { stroke(255, 10); strokeWeight(0.5); let step = width / 20; for (let x = 0; x < width; x += step) { line(x, 0, x, height); } for (let y = 0; y < height; y += step) { line(0, y, width, y); } noStroke(); }

function windowResized() { let size = min(windowWidth, windowHeight); resizeCanvas(size, size); redraw(); }

上傳前的檢查清單

[ ] 作品是確定性的(同一個 fxhash 永遠產生同一個結果)
[ ] 沒有使用 Math.random()(改用 fxrand())
[ ] 沒有使用 p5.js 的 random()(改用自訂的 fxRandom())
[ ] 有呼叫 fxpreview() 產生預覽圖
[ ] 有設定 $fxhashFeatures
[ ] 在不同的視窗大小下都能正確顯示
[ ] 載入速度合理(< 5 秒)
[ ] 已用不同的 hash 測試過多次,確認多樣性足夠

小結

生成式藝術 NFT 是一個把「程式碼即藝術」推到極致的領域。它讓創意編程者有了一個嶄新的展示和分發作品的方式。

但請記住:好的作品比好的行銷重要。先磨練你的創作技巧,培養你的美學品味,建立你在社群中的聲譽。NFT 只是一個管道,核心永遠是作品本身。

如果你已經在做 p5.js 創作,花一個週末把作品改成 fxhash 格式,發布上去看看。即使沒人買,你也學到了新東西,而且你的作品永遠存在鏈上——這本身就很酷。

延伸閱讀