home about projects profile
互動與遊戲 2026 系統與網站開發 資料分析與視覺化 硬體
Cyber Booth
// SYS 官方專案 現場展覽

Cyber Booth

由 touchdesigner 擴展的模組化拍貼機

by jx06t

// project_overview

Cyber Booth 是一款專為實體活動設計的互動拍貼系統。打破傳統靜態拍照的限制,結合了強大的影像處理引擎與直覺的操作介面,為參與者帶來充滿科技感與創意的專屬回憶。 Cyber Booth 採用了兼具效能與擴充性的模組化設計,透過獨立的配置文件(manifest.json)與視覺元件,無論是更換全新的互動特效、調整照片槽位排版 (Layout),或是新增動態文字與浮水印,都能以「抽換模組」的方式快速部署。

// tech_stack
TouchDesigner Python Node.js Socket.io Supabase Vercel Tailwind CSS
// keywords
# Cyber Booth # 互動拍貼系統 # 即時視覺特效
幫這個專案投票!
// project_details

Cyber Booth — 互動拍貼系統

v3.4.0 · TouchDesigner + Node.js + Sharp + Cloudflare R2 + Supabase + Vercel


這是什麼

Cyber Booth 是一套結合高性能視覺引擎的互動拍貼機系統,專為實體活動設計。

攝影機畫面進入 TouchDesigner 後,即時套用長曝光累積、仙女棒粒子軌跡等特效,再透過 NDI 虛擬攝影機串流到瀏覽器操作介面——使用者看到的預覽畫面,就是最終合成照片的效果。拍攝完成後,Node.js 背景執行 Sharp 影像引擎,自動裁切對齊四張照片、疊加相框、插入動態日期與 QR code,輸出成品。整個流程從按下快門到成品上雲,完全自動化。

NOTE

因為一開始這只是個偏概念性的小 demo 就隨便寫,但後來想要維護時又太忙所以就直接無腦叫 ai 重構,但總之重構的不是很好所以現在有點半屎山狀態。

WARNING

本介紹文章內的部分通訊流程、配置方法、所需設備等段落為舊版本之敘述,將盡快更新。


核心功能

即時視覺特效
仙女棒粒子軌跡、長曝光幀疊加,完全由 TouchDesigner Feedback TOP 管理,Node.js 不介入幀邏輯。

自訂相框佈局
透過 manifest.json 配置畫布尺寸、照片槽位、文字 widget 與 QR code,無需修改程式碼。支援多個 layout variant,現場可即時切換。

手機遙控面板
同網域下手機訪問 /remote,即可控制快門與選片,無需觸碰主機。桌面與手機介面共享同一套狀態機,透過 Socket.io 即時同步。

私有雲端取圖
整合 Cloudflare R2(媒體儲存)與 Supabase(資料庫)、Vercel(下載頁面)。Session ID 加密混淆防止他人掃描,檔案 24 小時後自動清理。支援照片與影片上傳。

提前結束補齊
拍不足四張可隨時停止,系統自動循環補齊至四張,保證輸出完整的四格拍貼成品。

多模組 / 多佈局切換
modules/ 中放置不同視覺主題(各含獨立 .tox 特效與 manifest.json),現場可即時切換,無需重啟服務。


系統架構

系統有兩條獨立的資料流,控制與視訊互不干擾:

控制流Browser ↔ Node.js ↔ TouchDesigner,透過 Socket.io(雙向狀態同步)與 HTTP REST(指令下發)協作。

視訊流攝影機 → TD 特效處理 → NDI Out → NDI 6 Webcam 虛擬驅動 → 瀏覽器 getUserMedia,單向傳遞,瀏覽器預覽的畫面即已包含所有即時特效。

存檔流程由 TD 主動觸發:TD 完成存檔後以 POST /td_state_update 通知 Node.js 進入 REVIEWING 狀態,附帶 currentFile 檔名,Node.js 附加 previewUrl 後廣播給所有前端。


螢幕截圖

拍攝範例圖


standard
dither
sparkle

拍攝狀態機

Node.js 持有 currentSystemState 作為唯一狀態來源,透過 status_update Socket 事件廣播給所有連線裝置。

State 名稱 說明
2 IDLE 待機,等待觸發快門
3 COUNTDOWN 3 秒倒數,Node.js 以 setTimeout 管理
0 RECORDING 長曝光快門開啟,TD Feedback TOP 開始幀疊加
1 PROCESSING 等待 TD 存檔,或 Sharp 影像合成進行中
4 REVIEWING 顯示預覽圖,等待使用者 KEEP / RETAKE
5 FINISHED 合成完成,顯示成品與 QR code

狀態流轉:

IDLE → trigger_shot → COUNTDOWN → 3s →
  ├─ recording → RECORDING → STOP → PROCESSING → REVIEWING
  └─ snapshot  → PROCESSING → REVIEWING

REVIEWING → KEEP (×4 or finish_early) → PROCESSING → FINISHED
REVIEWING → RETAKE → IDLE

兩種拍攝模式:

  • 錄影模式(recording):倒數後快門開啟,經過設定秒數或使用者手動按 STOP 結束,TD 凍結畫面並存檔。
  • 快拍模式(snapshot):倒數後 TD 自動擷取當前幀存檔,不經過 state 0。

若模組支援多個模式可在同一個 layout 中自由組合,以 layout 作為使用者可選擇的基本單位(例如:若要提供兩種模式讓使用者選擇,須建立兩個 layout)。


影像合成

合成引擎 composer.jsPromise.all 並行裁切四張照片,依 manifest 疊加相框 overlay,渲染動態文字與 QR code widget,最後只執行一次 Sharp pipeline 輸出(toBuffer() → 寫檔),效能最大化。

layout 物件由 server.jsactiveLayout 傳入,overlay_path 在載入時即已轉換為絕對路徑,合成引擎不處理路徑解析。

Widget 支援類型:

  • type: "text":靜態字串或動態佔位符({CURRENT_DATE}
  • type: "image":靜態圖片路徑(如 Logo)或動態生成({QR_CODE}

快速上手

安裝需求: TouchDesigner 2023+、Node.js LTS、NDI 6 Tools、攝影機輸入源(WebCam / DSLR 採集卡 / VDO.ninja)。

啟動步驟:

  1. 開啟 TD/main.toe,確認影像輸入源與 NDI 輸出正確,Web Server DAT(Port 8080)已啟動。
  2. 開啟 NDI 6 Webcam 工具,於 Video 1 選擇 TouchDesigner 作為輸入來源。
  3. 執行 npm install,再執行 npm run start,伺服器監聽 Port 5000。
  4. 瀏覽器訪問 http://localhost:5000 開始拍攝;手機遙控訪問 http://<本機IP>:5000/remote

所有拍攝照片與合成成品預設存放於 sessions/ 資料夾,不會上傳至公開雲端。


自訂相框佈局

參考 modules/cyber_standard/manifest.json,複製後修改。在 server.jsloadModuleManifest() 中指定模組名稱即可套用。

{
  "name": "Dither",
  "preview_image": "preview.jpg",
  "capabilities": {
    "capture": {
      "modes": [
        "instant",
        "timed"
      ],
      "timedDurations": [
        3
      ]
    },
    "output": {
      "types": [
        "image",
        "video"
      ]
    }
  },
  "defaultLayout": "4v",
  "layouts": [
    {
      "id": "4v",
      "label": "4 Shots Vertical",
      "preview": "preview_4v.jpg",
      "canvas": {
        "w": 1500,
        "h": 4000,
        "bg": "#000000"
      },
      "overlay_path": "overlay.png",
      "slots": [
        {
          "x": 110,
          "y": 120,
          "w": 1280,
          "h": 720,
          "capture": "instant",
          "type": "image"
        },
        {
          "x": 110,
          "y": 940,
          "w": 1280,
          "h": 720,
          "capture": "timed",
          "timedDuration": 3,
          "type": "video"
        }
      ],
      "widgets": [
        {
          "id": "date_display",
          "type": "text",
          "content": "{CURRENT_DATE}",
          "x": 990,
          "y": 3800,
          "fontSize": 64,
          "color": "#FFFFFF",
          "fontFamily": "Arial"
        },
        {
          "id": "session_qr",
          "type": "image",
          "content": "{QR_CODE}",
          "x": 110,
          "y": 3700,
          "w": 200,
          "h": 200
        }
      ]
    }
  ]
}

overlay_path 填寫相對於 manifest 資料夾的路徑,loadModuleManifest() 會自動轉換為絕對路徑。


雲端取圖設定(選配)

雲端功能需設定以下元件:Cloudflare R2(媒體儲存)、Supabase(資料庫 + 定時清理)、Vercel(下載頁面)。

Cloudflare R2

媒體儲存(照片與影片)統一上傳至 Cloudflare R2,相較 Supabase Storage 具備更好的頻寬成本與影片大檔案支援。

  1. 在 Cloudflare 建立 R2 Bucket(例如命名為 cyber-booth-media)。
  2. 建立具有 Object Read / Object Write 權限的 API Token(Access Key ID + Secret Access Key)。
  3. 開啟 Bucket 的公開存取(Custom Domain 或 r2.dev 子網域),取得公開基底 URL。
  4. .env 中填入:
R2_ACCOUNT_ID = your_cloudflare_account_id
R2_ACCESS_KEY_ID = your_access_key_id
R2_SECRET_ACCESS_KEY = your_secret_access_key
R2_BUCKET_NAME = cyber-booth-media
PUBLIC_R2_CDN_URL = https://your-public-domain.example.com

uploader.js 使用 AWS SDK v3(@aws-sdk/client-s3)對接 R2 的 S3 相容 API,endpoint 設為 https://<ACCOUNT_ID>.r2.cloudflarestorage.com

Supabase 資料庫

Supabase 保留資料庫功能,不再儲存媒體檔案。

  1. 建立資料表 collages,欄位:id uuidsession_id textcreated_at timestamptz(自動索引)。
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_SERVICE_ROLE_KEY=your_service_role_key

Vercel 下載頁面

  1. 修改 public-viewer/index.html 中的公開媒體基底 URL 為 R2 的公開網域。
  2. public-viewer/ 部署至 Vercel。
  3. 修改 uploader.js 中的 VERCEL_DOMAIN 為部署後的公開網址。
  4. 下載頁面透過 Blob 下載機制強制觸發檔案儲存對話框,支援 Web Share API 直接分享至通訊軟體。

Session 安全

每次重置產生唯一 Session ID:ssn_{Date.now()}_{8位亂數},防止使用者猜測他人 QR code 連結。

未設定 SUPABASE_URL 或 R2 相關環境變數時,系統自動以本機模式運作,所有輸出僅存於 sessions/ 資料夾,不影響拍攝功能。


環境變數總覽

變數 必填 說明
SUPABASE_URL 選填 Supabase 專案 URL,未設定則本機模式
SUPABASE_SERVICE_ROLE_KEY 選填 Supabase Service Role Key
R2_ACCOUNT_ID 選填 Cloudflare 帳號 ID
R2_ACCESS_KEY_ID 選填 R2 API Token Access Key
R2_SECRET_ACCESS_KEY 選填 R2 API Token Secret
R2_BUCKET_NAME 選填 R2 Bucket 名稱
PUBLIC_R2_CDN_URL 選填 R2 公開媒體基底 URL

技術棧

  • Visuals: TouchDesigner, Python (TD Scripts)
  • Backend: Node.js, Express, Socket.io
  • Image Processing: Sharp
  • Frontend: Vanilla JS, Tailwind CSS, QRious
  • Storage: Cloudflare R2(照片 + 影片)
  • Database & Functions: Supabase(DB, Edge Functions, pg_cron)
  • Hosting: Vercel

// Comments

載入留言…
// similar_projects

latent

// 很酷的網站

AstroReactTypeScript+10
系統與網站開發

2.5D

// NO_SUBTITLE

JavaScriptHTML5 CanvasSupabase+1
互動與遊戲

md2pdf

// 高度自訂化的 Markdown 轉檔工具

ViteReactTypeScript+5
工具與自動化