这是一份提交作品,参加的是Sanity Challenge,第二赛道:Vibe-Code Something Strange


我以一个问题开始了这场黑客松,这个问题看似简单:为什么数字博物馆仍然被设计得像电商目录?

为什么我们浏览人类最伟大的突破、永恒的艺术作品和计算革命时,看到的却是扁平的矩形卡片网格、分页按钮和筛选侧边栏?

几天后,我穿行于一个用实时 WebGL 渲染的完整 3D 空间博物馆中,用自定义的屏幕 D-Pad 在大理石基座之间移动,凌晨 2 点在 Vercel 上与 Next.js 编译器阻碍搏斗,搭建了一个世界内的空间控制室,还教会了 Sanity 的 Content Lake 记住哪些历史文物是“存活”、“冷却”还是“死亡”。

这种升级大概已经能告诉你这个项目是怎么一路走来的了。

最终成果是The Living Museum:一个由 Sanity 驱动的 3D 空间宇宙。

The Living Museum 没有把人类历史、艺术和创新当作电商信息流来浏览,而是把集体记忆变成了一个可探索的物理世界。

  • 你穿行于一个个连续的展厅。
  • 文物矗立在发光的实体基座上。
  • 靠近展品会暂停时间,并打开它的策展来源信息。
  • 一个氛围开关就能把天窗从白昼照明切换成戏剧性的午夜圣殿。
  • 而在这一切之下,Sanity 不只是存储文本和图像——它充当整个 3D 宇宙的空间记忆与生命周期引擎。

这个项目背后的问题最终变成了:

如果结构化内容拥有物理地理和生物生命力,会怎样?


🔗 项目链接


🏛️ 我构建了什么

The Living Museum是一个可行走的 3D 空间档案库,展品从 Sanity Content Lake 动态渲染到交互式 Three.js 环境中。

博物馆按顺序分为四个主题展厅:

展厅 主题 核心展品与体验
1 号厅:发明 人类突破 轮子、印刷机、蒸汽机、电话、互联网。
2 号厅:艺术 视觉与文化遗产 古典希腊雕塑、原住民民间工艺、传统印度艺术,以及数字装置。
3 号厅:历史 计算与里程碑 机械计算器、早期真空管计算机、阿波罗遥测数据,以及数字时代。
4 号厅:未来 前沿视界 神经网络、量子计算核心、星际探测器,以及自主系统。

数据流架构

我不想只是把博物馆的文本复制进 CMS 再渲染成卡片。我的目标是让 Sanity 成为这个 3D 世界的空间操作系统:

       +-----------------------------------------------+
       |             Sanity Content Lake               |
       |  (Schemas, 3D Coordinates, Vitality, State)   |
       +-----------------------+-----------------------+
                               |
               GROQ Query & Asset Pipelines
                               |
                               v
       +-----------------------------------------------+
       |             Next.js 16 (App Router)           |
       |         Turbopack + React 19 Client           |
       +-------+-------------------------------+-------+
               |                               |
               v                               v
    +----------------------+       +-----------------------+
    | Three.js WebGL Engine|       | React UI Overlay      |
    | - Dynamic Pedestals  |       | - 4-Button D-Pad Walk |
    | - Lighting & Shadows |       | - Interactive Modal   |
    | - Camera Raycasting  |       | - Dark Mode Ambient   |
    | - Spatial Proximity  |       | - Chamber Fast Travel |
    +----------------------+       +-----------------------+
               ^                               ^
               |                               |
               +---------------+---------------+
                               |
                               v
            +------------------------------------+
            |    Mission Control Telemetry Room  |
            |  (/control-room) - Live Lifecycle  |
            |   ACTIVE -> COOLING -> ARCHIVED    |
            +------------------------------------+
  • Three.js负责渲染物理建筑结构、空间光照和响应式网格。
  • Sanity告诉这个世界什么该放在哪里、它有多亮、它在 3D 空间中的坐标,以及它的生命周期健康状况。
  • Next.js以零延迟的状态同步连接两者。

🎮 演示

参观 The Living Museum 是一次可触摸、有氛围、即时可感的体验:

  1. 进入大厅:直接出生在1 号厅(发明),环境日光透过天窗洒落。
  2. 自然探索:使用键盘控制行走(W-A-S-D),或触摸/点击4 按钮方向键(上、下、左、右)在走廊中滑行。
  3. 锁定展品:直接点击任意发光的展台或展品(例如蒸汽机或轮子)。
  4. 策展式查看:一个优雅的查看弹窗打开,暂停移动,并渲染高分辨率图像、历史来源、创作者记录,以及来自 Sanity 的实时活力评分。
  5. 氛围夜间模式:切换导航栏中的月亮图标,看日光消融为深邃的夜间圣殿,展品聚光灯聚焦其上。
  6. 展厅传送:使用展厅选择器瞬间跳转到艺术、历史和未来。
  7. 任务控制:导航至/control-room以查看实时展品遥测数据、调整活力评分,并触发复苏协议。

🎥 1 分钟导览视频


📸 走进 The Living Museum

1. 查看蒸汽机(交互式弹窗)

点击展品会显示其完整的历史文档、创作者元数据和实时 Sanity 生命周期遥测数据。

Steam Engine Modal


2. 走过 2 号展厅(艺术画廊)

以流畅的 60 FPS 穿行于古典雕塑、印度民间手工艺和视觉艺术之间。

Art Gallery Walking


3. 氛围夜间模式

一键切换灯光,将氛围转变为戏剧性的闭馆后博物馆导览。

Night Mode


4. 任务控制室与遥测编辑器

我们自定义的虚拟世界内空间关卡编辑器,用于检查活力指数和生命周期状态。

Mission Control Room


5. 作为世界引擎的 Sanity Studio CMS

原生 Sanity Studio 嵌入于/studio,管理 40 多条博物馆记录。

Sanity Studio


💻 代码

整个代码库是开源的:

🏛️ 活着的博物馆 — Sanity 挑战赛(路径 2)

The Living Museum Cover Banner

Live Demo GitHub Sanity CMS

Sanity 挑战赛路径 2:Vibe-Code Something Strange
一个交互式 3D 数字博物馆,软件架构、复古里程碑和技术遗物被永久化为实时展品,完全通过Sanity Content Lake管理。


🌐 线上部署


✨ 功能

1. 🚶 3D 第一人称行走与方向键控制

  • 以持续的 60 FPS 移动穿行于宏伟的展览大厅,使用W-A-S-D 键或屏幕上的4 键方向键 D-Pad(上、下、左、右)。
  • OrbitControls 带阻尼摩擦,带来自然的观看和探索体验。

2. 🏛️ 4 个主题展厅

  • 1 号展厅:发明— 奠基性突破(轮子、蒸汽机、电话、灯泡、计算机、互联网)。
  • 2 号展厅:艺术— 古典大理石雕塑、洞穴壁画、传统印度艺术和数字创作。
  • 3 号展厅:历史— 历史计算机遗物和编程里程碑。
  • 4 号展厅:未来— 前沿 AI……

核心技术栈:

  • Next.js 16.3.8(App Router 与 Turbopack)
  • React 19 与 TypeScript
  • Three.js 与 OrbitControls(WebGL 渲染与射线投射)
  • Sanity Content Lake(@sanity/client,next-sanity,@sanity/image-url)
  • Sanity Studio v3(嵌入于/studio)
  • Tailwind CSS 与 Lucide Icons(未来主义玻璃拟态 UI)
  • Vercel(全球边缘部署)

🛠️ 构建活着的博物馆:Vibe-Coding 奥德赛

这个项目是在一次密集的 vibe-coding 冲刺中构建的,使用了Google Antigravity和自主智能体结对编程。

这个项目并非源自某个神奇的提示词。它经历了密集的试错、架构上的转向、令人捧腹的 AI 幻觉,以及深度的技术路线修正。

以下是幕后真正发生的事情:

1. 它最初只是一个简单的作品查看器

最初的想法很朴素:一个标准的 Next.js 画廊应用,后端用 Sanity。你选择一个标签(比如 "Invention"),卡片就会带着漂亮的 CSS 动画滑入。

它很干净。它很实用。
而且它无聊透顶。

为什么要参加路径二:用氛围编程做点奇怪的东西如果你只是要再搭一个卡片网格的话?

我想要物理存在感。我想要走向一个想法。我向智能体发出提示:

"删掉网格。用 Three.js 把视口变成一个 3D 大厅。Sanity 里的每个文档都必须变成一个实体基座,上面悬浮着一件作品。"

几分钟内,第一个 3D 房间出现了。而真正的疯狂也由此开始。


2. 从平面网格到物理世界:4 个房间

3D 画布一上线,我们就意识到把 40 件展品一股脑塞进一个巨大的房间里在视觉上是一片混乱。所有东西都在争夺注意力,性能也一落千丈。

我们需要空间层级。
我们把展品分成了四个不同的展厅:

  1. 发明($X = -20 \text{ to } 0$)
  2. 艺术($X = 10 \text{ to } 30$)
  3. 历史($X = 40 \text{ to } 60$)
  4. 未来($X = 70 \text{ to } 90$)

通过沿 Z 轴和 X 轴分配连续的坐标空间,访客既可以在各个时代之间实际行走,也可以点击房间选择器平滑地滑过数个世纪。


3. AI 很有用——但也自信地犯错

用 AI 智能体做氛围编程非常棒,但模型在 3D 空间里会自信地、滑稽地犯错:

  • 基座倒置事件:早期,我让智能体给每件展品加上聚光灯。智能体没有把聚光灯从天花板向下照向基座,而是把灯放在 $Y = -10$ 处,朝上穿过地板照射。基座看起来漆黑一片,但天花板却像超新星一样发光。
  • 无限虚空 bug:在实现相机重置逻辑时,智能体把相机的 Z 轴目标设成了undefined在过渡期间。摄像机立即以曲速加速进入无限黑暗空间。我不得不重新加载浏览器才能再次找到地球。
  • 隐形墙:我要求智能体阻止玩家穿墙。智能体尽职地添加了碰撞网格——但不小心将它们渲染成了实心洋红色方块,把玩家完全包裹在一个盒子里。

航向修正:我们创建了严格的模块化边界:Three.js 数学和摄像机边界被隔离在MuseumScene.tsx内部,而 React 负责处理 UI 叠加层和 Sanity 订阅。


4. 驯服移动:4 按钮方向 D-Pad

桌面用户喜欢 WASD 键盘移动,但休闲访客和移动端测试者却感到吃力。

键盘在 iPad 或触摸屏上无法使用,而仅依赖鼠标拖拽会让不理解 3D 摄像机矩阵的用户迷失方向。

我们设计了一个悬浮的、触感式的4 按钮方向 D-Pad(上、下、左、右)。按住箭头可以平滑地向前、向后平移摄像机,或左右横移,并带有电影般的阻尼效果。

突然之间,任何人都可以单手在博物馆中漫步了。


5. Vercel 与 Turbopack 的熔炉

使用 Next.js 16 和 Sanity Studio 部署一个前沿的 WebGL 应用是一场严峻考验:

  1. Next.js 16 安全检查:由于上游安全公告,Vercel 抛出了一个硬性部署错误,阻止了较旧的 Next.js 16 候选版本。我们将依赖升级到 Next.js16.3.8以满足部署健康检查。
  2. 严格的@sanity/ui类型不匹配:在 Turbopack 的生产构建期间,<Badge tone="...">来自@sanity/ui触发了严格的 TypeScript 错误,因为 tone prop 被类型化为内部枚举。我们用轻量级、零依赖的 Tailwind 徽章替换了笨重的外部 UI 徽章。
  3. CORS 限制:嵌入在/studio的 Sanity Studio 最初因浏览器 CORS 策略而无法加载数据集。我们运行了npx sanity cors add "https://*.vercel.app" --credentials以使用完整身份验证令牌将生产部署加入白名单。

6. 教会 Sanity 记住生、死与活力

这成为了我们参赛作品的核心哲学。

在普通的 CMS 中,内容是静态文本:标题、slug、正文段落。
在活着的博物馆中,展品是一个活的有机体。

我们用自定义遥测对象扩展了 Sanity schema:

{
  name: 'control',
  type: 'object',
  title: '博物馆遥测',
  字段: [
    {
      name: 'lifecycle',
      type: 'string',
      title: '生命周期状态',
      options: { list: ['ACTIVE', 'COOLING', 'ARCHIVED', 'REVIVED'] }
    },
    {
      name: 'vitality',
      type: 'number',
      title: '活力指数 (0 - 100)'
    },
    { name: 'x', type: 'number', title: '空间 X 坐标' },
    { name: 'y', type: 'number', title: '空间 Y 坐标' },
    { name: 'z', type: 'number', title: '空间 Z 坐标' }
  ]
}
  • 如果展品的活力降至 20 以下,其基座灯光会变暗并闪烁琥珀色(COOLING)。
  • 如果降至 0,文物将进入ARCHIVED状态。
  • 策展人可以触发复活协议,通过 Sanity patches 将活力重新注入文档。

Sanity 不仅仅是在提供内容——它正在运行这个世界的心跳。


7. 构建我们自己的空间编辑器:任务控制室

在代码中更改坐标并重新部署太慢了。

我们构建了/control-room:应用程序内一个专门的任务控制仪表盘。

策展人可以查看全部 4 个房间中的全部 40 件展品,按生命周期状态筛选,实时调整活力滑块,并触发即时复活。

每一次更改都会 patch Sanity Content Lake,进而立即改变 3D 场景中基座的照明。


📊 Sanity 项目详情

整个博物馆都建立在我们公开的 Sanity Content Lake 之上:

  • 项目 ID: iidfd4sp
  • 数据集: production
  • 公开端点:
  https://iidfd4sp.api.sanity.io/v2024-01-01/data/query/production?query=*[_type == "exhibit"]

主要的 GROQ 查询

以下是用于填充 3D 博物馆的实时查询:

*[_type == "exhibit"] | order(_createdAt asc) {
  _id,
  name,
  category,
  creator,
  era,
  description,
  "imageUrl": image.asset->url,
  "control": {
    "lifecycle": coalesce(control.lifecycle, "ACTIVE"),
    "vitality": coalesce(control.vitality, 100),
    "x": control.x,
    "y": control.y,
    "z": control.z
  }
}

🤖 智能体会话与方法论

  • 环境:Google Antigravity Agentic IDE
  • 模型引擎:Gemini 2.5 Flash / Pro
  • 会话工作流:
    • 架构脚手架与 Next.js 16 集成。
    • Three.js 程序化几何体与光照算法。
    • 通过自动化脚本(upload_to_sanity.js)为 Sanity Content Lake 数据集填充种子数据。
    • 使用 Playwright 进行无头浏览器测试与实时屏幕验证。
    • 通过 FFmpeg 和 AI 配音引擎生成视频并合成旁白。

所有转录文本和智能体交互均被记录并保存在仓库历史中的/docs/agent-sessions下。


🌌 最终思考

参与路径二:Vibe-Code Something Strange让我重新思考了未来十年网络可能的样子。

二十年来,网络一直被 2D 文档页面所主导。但随着 GPU、WebGL 和生成式 AI 的融合,结构化数据不再需要被困在扁平的网格之中。

当你把一个敏捷的内容引擎如Sanity与一个空间渲染层如Three.js配对时,内容就不再是屏幕上的文字。

它变成了一个你可以漫步其中的地方。


💬 让我们讨论一下吧!

  • 你会把哪件历史文物或技术里程碑添加到 4 号房间?
  • 你是否尝试过用 Sanity 处理空间坐标或游戏状态?

在下方评论区留下你的问题、想法或反馈吧!让我们一起突破结构化内容的能力边界。🚀