这是一份提交作品,参加的是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 宇宙的空间记忆与生命周期引擎。
这个项目背后的问题最终变成了:
如果结构化内容拥有物理地理和生物生命力,会怎样?
🔗 项目链接
- 🌐在线 3D 博物馆: https://sanity2-two.vercel.app
- 🎛️任务控制室: https://sanity2-two.vercel.app/control-room
- 🎨Sanity Studio CMS: https://sanity2-two.vercel.app/studio
- 💻GitHub 仓库: github.com/Ayushgupta1715/sanity-museum
- 📡公开 Sanity API 查询: 探索公开数据集
- 🎬演示视频导览: YouTube:The Living Museum Tour(1 分钟)
🏛️ 我构建了什么
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 号厅(发明),环境日光透过天窗洒落。
- 自然探索:使用键盘控制行走(W-A-S-D),或触摸/点击4 按钮方向键(上、下、左、右)在走廊中滑行。
- 锁定展品:直接点击任意发光的展台或展品(例如蒸汽机或轮子)。
- 策展式查看:一个优雅的查看弹窗打开,暂停移动,并渲染高分辨率图像、历史来源、创作者记录,以及来自 Sanity 的实时活力评分。
- 氛围夜间模式:切换导航栏中的月亮图标,看日光消融为深邃的夜间圣殿,展品聚光灯聚焦其上。
- 展厅传送:使用展厅选择器瞬间跳转到艺术、历史和未来。
-
任务控制:导航至
/control-room以查看实时展品遥测数据、调整活力评分,并触发复苏协议。
🎥 1 分钟导览视频
📸 走进 The Living Museum
1. 查看蒸汽机(交互式弹窗)
点击展品会显示其完整的历史文档、创作者元数据和实时 Sanity 生命周期遥测数据。
2. 走过 2 号展厅(艺术画廊)
以流畅的 60 FPS 穿行于古典雕塑、印度民间手工艺和视觉艺术之间。
3. 氛围夜间模式
一键切换灯光,将氛围转变为戏剧性的闭馆后博物馆导览。
4. 任务控制室与遥测编辑器
我们自定义的虚拟世界内空间关卡编辑器,用于检查活力指数和生命周期状态。
5. 作为世界引擎的 Sanity Studio CMS
原生 Sanity Studio 嵌入于/studio,管理 40 多条博物馆记录。
💻 代码
整个代码库是开源的:
🏛️ 活着的博物馆 — Sanity 挑战赛(路径 2)
Sanity 挑战赛路径 2:Vibe-Code Something Strange
一个交互式 3D 数字博物馆,软件架构、复古里程碑和技术遗物被永久化为实时展品,完全通过Sanity Content Lake管理。
🌐 线上部署
- 生产 Web 应用: https://sanity2-two.vercel.app
- Sanity Studio CMS: https://sanity2-two.vercel.app/studio
- 任务控制室: https://sanity2-two.vercel.app/control-room
✨ 功能
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 件展品一股脑塞进一个巨大的房间里在视觉上是一片混乱。所有东西都在争夺注意力,性能也一落千丈。
我们需要空间层级。
我们把展品分成了四个不同的展厅:
- 发明($X = -20 \text{ to } 0$)
- 艺术($X = 10 \text{ to } 30$)
- 历史($X = 40 \text{ to } 60$)
- 未来($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 应用是一场严峻考验:
-
Next.js 16 安全检查:由于上游安全公告,Vercel 抛出了一个硬性部署错误,阻止了较旧的 Next.js 16 候选版本。我们将依赖升级到 Next.js
16.3.8以满足部署健康检查。 -
严格的
@sanity/ui类型不匹配:在 Turbopack 的生产构建期间,<Badge tone="...">来自@sanity/ui触发了严格的 TypeScript 错误,因为 tone prop 被类型化为内部枚举。我们用轻量级、零依赖的 Tailwind 徽章替换了笨重的外部 UI 徽章。 -
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 处理空间坐标或游戏状态?
在下方评论区留下你的问题、想法或反馈吧!让我们一起突破结构化内容的能力边界。🚀





