AI 新視界 AI WorldviewAI 新視界 AI Worldview
申請帳號 / 登入
實作筆記0 個來源Claude fable 5.1 撰寫

拆解 3D 政見網站「HEART 政策島」:Three.js、單一 JSON,以及程式碼裡的 AI 協作痕跡

台北市長參選人競選官網的 3D 政策頁,自寫程式約 7,400 行、沒有打包工具、全部內容在一個 159 KB 的 JSON。資料檔裡的 Claude 輸出目錄、待確認狀態欄位,以及被官網收錄的外掛在 GitHub 的提交紀錄,顯示它在資料整理、視覺規範與外掛收錄三個環節都用了 AI 協作。本文只談技術,不評論政見。

本文重點

  1. 拆解台北市長參選人競選官網的 3D 政策頁,分析其技術架構與 AI 協作痕跡。
  2. 自寫程式約 7,400 行,內容存於 159 KB 的 JSON,資料檔留有 Claude 輸出目錄。
  3. 無 GPU 環境效能極差,第三方外掛收錄缺乏明確授權條款,仿效時需注意。
本站上線
2026/10/24 下午11:51
首次報導
2026/10/24 下午11:51
圖:FLUX 3 Image Klein(地端,seed 613,28 步);caption: Studio photograph, a highly detailed, polished digital vector illustration presented in a wide, horizontal editorial layout. The scene depicts a stylized, abstract 3D cityscape constructed from fourteen distinct hexagonal islands arranged in a loose, organic cluster that mirrors the geographic silhouette of a metropolitan area. The islands are rendered in deep navy blue and slate grey, with smooth
實作筆記|本站實際操作|由 AI 助理撰寫|未經人工審閱。文中的指令、輸出與數字都取自本站實際執行的紀錄與錄影, 文字由 AI 助理依這些紀錄整理;安裝方式與版本以當天為準,之後可能變動。發現錯誤請用文末的「回報」。

台北市長參選人沈伯洋的競選官網上有一頁「HEART 政策島」:14 項政策主題排成 14 塊六邊形小島,拼成台北市的輪廓,島上有建築、行人、日夜與季節,點任何一棟建築就看到對應的政見細項與出處。本站站長看到後的第一個反應是「這也是用 AI 寫的吧」。我們把整頁公開的程式碼與資料檔抓下來拆開,從架構、資料、渲染到效能逐項看,並查到了它與 AI 協作的直接證據。以下只談技術,不評論任何政見內容;實測影片公開後會嵌在本文最下方。

這一頁解決什麼問題

一份市長政見通常是幾十頁的清單,讀者看不出項目之間的關係。這一頁把 94 個細項分到 14 個主題,再用 20 條「協作線」標出哪些主題互相支援(例如「先替公務員減壓,再推動各項政策」這條線連到 13 座島),每條線與每棟建築都附出處類型:官網政見、候選人受訪、或政策主題串聯。訪客不必讀完清單,也能從一個改變看到它牽動哪些其他項目。

面向傳統政見清單頁政策島
呈現文字分類清單3D 城市,一島一主題,一建築一細項
關係沒有20 條跨主題協作線,可逐條點亮
出處通常沒有每個細項與每條線標明來源與證據等級
無法用 3D 時不適用右側清單可完整文字操作,手機版改成底部抽屜

架構:沒有打包工具,一個 JSON 驅動整座島

整頁是靜態檔案。HTML 只有 11 KB,用瀏覽器原生的 importmap 指向自帶的 Three.js r160,所有程式都是原生 ES module,沒有 Webpack、Vite 這類打包步驟,也沒有 React 之類的框架。競選官網的其他頁面是 Vite 建置的,這一頁刻意獨立出來,連統計事件的程式都註明「這頁在 Vite 之外,所以另外寫一份」。

自寫的程式共 13 個檔案、約 7,400 行,分工如下:

檔案行數負責什麼
app.js2,302入口:介面、相機、卡片、搜尋、導覽、計時
decor.js1,431裝飾物:樹、碼頭、小島、岸邊小物
people.js752Q 版行人:13 種角色、走路動畫、搬運物件
world.js704六邊形島體、橋、行人路網、建築配置
scene-models.js674每項政策的象徵建築(例如「彈性上下班鐘塔」)
environment.js557天空、光、水、雲、地形、日夜變化
kit.js133零件庫:同類幾何合成一個 InstancedMesh
icons.js、analytics.js、models-extra.js、decor-items.js少量圖示、GA4 事件、額外模型與清單
ui.css1,545全部樣式,含手機版與減少動態效果

內容全部在一個 159 KB 的 policies.json 裡:14 座島、每座島的建築、20 條協作線、3 段受訪引述、一天的時段腳本、任期時間軸、78 秒導覽的分鏡,以及調色盤與版面規則。文字欄位一律是中英雙語物件,所以在網址加上 lang=en 就是英文版;另一個參數 ratio=9x16 會把版面改成直式,顯然是為了錄短影音準備的。

程式碼與資料的分離做得很徹底:建築用明確的 id 對應政策細項,檔案註解寫明「用明確 ID,避免陣列順序改變就悄悄換掉政策的視覺意義」。每個細項帶 facts、phase(時程與依據)、sources、status;每條協作線帶 evidenceTier(證據等級)與引述編號。換句話說,「這一條有沒有查證過」是寫在資料層的欄位,不是寫在程式裡的判斷。

渲染:Instancing、合批與固定亂數

場景裡有上百棵樹、幾十棟建築、60 個會走路的行人,但繪製次數壓得很低,靠的是三個做法:

  • 零件庫合批:kit.js 把所有靜態零件依幾何種類(方塊、圓柱、圓錐、球)收集起來,每一種只畫一次 InstancedMesh。
  • 角色合併:行人的身體零件用 mergeGeometries 合成少數幾個網格,共用一個頂點色材質;換裝時不重新編譯材質。
  • 固定種子亂數:裝飾物的散佈用 mulberry32 這類可重現的亂數,每次載入的島長得一樣,也方便對照截圖。

地形用 SimplexNoise 產生,日夜是一組按分鐘排的關鍵影格(天空頂色、地平線、太陽色與強度、半球光、水色),拖動「台北人的一天」滑桿就在影格之間內插。文字標籤走 CSS2DRenderer,所以 14 個島名是真正的 HTML 元素,可以被選取、也能被搜尋引擎讀到。系統設定「減少動態效果」時,自動旋轉與導覽會停。

效能與相容性

我們用 Playwright 量測:

項目結果
首次載入27 個請求、傳輸 523 KB(解壓後 2.2 MB),其中 Three.js 佔 266 KB
1080p 畫面更新率Intel 內建顯示(Arrow Lake)59 fps
純軟體繪圖(無 GPU 的無頭瀏覽器)1 fps,幾乎不能用

結論是它需要 GPU,但不需要獨立顯卡;一般筆電與手機的內建繪圖足夠。手機版把右側清單改成可拉的底部抽屜,工具列只留全景、角色、清單、時間四個按鈕。無障礙上,3D 畫布標成 role="img" 並說明「完整文字操作請用政策清單」,清單本身是原生的 details 與 button,鍵盤可以完整操作。

它是用 AI 寫的嗎:找到的證據

我們沒有拿到官網的原始碼倉庫,所以主程式是不是 AI 寫的無法從外部百分之百確認。但公開檔案裡留下了四組痕跡:

1. 資料檔直接提到 Claude 的輸出目錄。 policies.json 的 renderPlan.paletteSource 欄位寫著「專案競選 CI:Claude outputs/…/交付說明_README.md」,也就是調色盤的依據是一份由 Claude 產出的交付說明。

2. 資料檔裡留著工作流程的狀態欄位。 meta.disclosure 是還沒填的「{{DISCLOSURE}}」佔位符;meta.stage 是「layout_and_data_awaiting_confirmation」;導覽分鏡的 status 是「storyboard_only」;受訪引述的 verification 是「provided_transcript_audio_unchecked」;協作線的 evidenceTier 有「transcript_pending」。18 個來源各自標了 status(14 個「read_official_page」、2 個「read_news_report」)與 confidence。這是典型的「模型先產生草稿、再由人逐項確認」流程留下的欄位,而且這份檔案是帶著這些狀態上線的。

3. 每個模組檔頭都有同一種「免責」註解。 decor.js、world.js、environment.js、people.js、scene-models.js 的第一、二行都在說明「這裡的東西純屬裝飾,不代表任何政策、地點或時程的主張」。這種在程式碼裡逐檔宣告邊界的寫法,是給 AI 代理的規格文件常見的要求,人工手寫的專案很少這樣做。

4. 被官網收錄的外掛,提交紀錄直接標了 Claude。 頁面上的「我的角色」功能(2,181 行的 heart-me.js)原本是第三方使用者腳本,作者 readper 於 10 月 23 日在 GitHub 發布,單一提交 2,118 行,提交訊息標註「Co-Authored-By: Claude Sonnet 5.5」並附 Claude Code 的工作階段連結;當天官網就取得作者同意收錄,檔頭還註明差異寫在 openspec 目錄下的規格檔。OpenSpec 是一套讓 AI 編碼代理照規格改程式的工作流程,這表示官網這邊的修改也是以規格驅動 AI 代理的方式進行。

同一個競選團隊在 10 月 23、24 日辦了線上「市政 debug 松」,座談題目就叫「AI 寫得出程式,寫不出城市」。綜合來看,這一頁至少在資料整理、視覺規範與外掛收錄三個環節確定用了 AI 協作;主程式的寫法(規格化註解、資料驅動、狀態欄位)也與這種流程一致。

照這個方法做一個,要做哪些事

這個案子值得學的不是 3D,而是分工方式。若要用 AI 助理做一個同類的互動資料頁,順序是:

1. 先定資料格式,再寫程式。 把內容做成一個 JSON:每個項目有穩定的 id、雙語文字、來源清單與證據等級,再加上工作流程的狀態欄位(待確認、僅分鏡、未查核)。這份格式就是給 AI 的規格,程式只負責把它畫出來。

2. 用原生 ES module 與 importmap。 不裝打包工具,AI 產生的每個模組可以直接在瀏覽器載入測試,檔案邊界清楚,之後要改哪一塊就只給哪一檔。

3. 每個模組檔頭寫明它可以做什麼、不可以做什麼。 這一頁的「純屬裝飾、不代表政策」就是例子;AI 在改裝飾物時不會順手去動政策資料。

4. 效能靠合批,不靠減內容。 同類幾何用 InstancedMesh,角色合併網格、共用材質,固定種子亂數讓結果可重現。

5. 留文字版與減少動態效果的路徑。 3D 只是加分,清單才是底線;這也是搜尋引擎與無障礙工具實際讀到的內容。

6. 把查證狀態留在資料裡,而不是刪掉。 上線時哪些還沒確認,資料檔自己會說;這比在程式裡硬寫判斷更容易由非工程人員維護。

人要做的部分很明確:確認每一條內容的出處與狀態。這份資料檔上線時還留著「待確認」的欄位,說明 AI 能把結構和草稿鋪好,但「這句話有沒有講過、出處對不對」仍然是人的工作。

限制

  • 它是純前端頁面,政策文字在 JSON 載入後才畫進清單;搜尋引擎能讀到的是標題、描述與島名標籤,細項內容不一定被收錄。
  • 沒有 GPU 的環境(部分遠端桌面、舊款虛擬機)會慢到不能用。
  • 第三方外掛被官網收錄的做法很有趣,但上游專案沒有附授權條款,只在檔頭註明作者透過社群訊息同意;要仿效的話,授權要先講清楚。

本文的程式碼與資料分析來自頁面公開的靜態檔案,量測在本站的機器上以 Playwright 完成;本站與該競選團隊沒有任何關係,文中不涉及政見內容的評價。

來源:佐證 0 筆,另有背景參考 3 筆

背景參考正文沒有引用,只是相關背景,不計入佐證數

  1. puma.taipei · HEART 政策島(競選官網頁面)
  2. github.com · readper/heart-island-me(被官網收錄的使用者腳本)
  3. puma.taipei · 市政 debug 松(活動頁)

這篇對你有用嗎?

登入會員後可以按反應與留言。登入

留言 0

  • 還沒有留言。

留言公開顯示暱稱,不顯示你的真實姓名與信箱。含多個連結或疑似廣告的留言會先經編輯確認;檢舉達 3 次的留言會自動隱藏。隱私政策

發現錯誤?

回報會進入待處理清單;核實後更正,並在文中註記更正紀錄。