# 像素画工具换调色板、改通信、加画廊

日期: 2026-07-06T01:50:25Z
摘要: 给像素画工具换了 NES 16 色调色板，通信从 SSE 改成 WebSocket 流式推送，加了作品画廊。三次改造解决的是同一个问题：让模型专注于画画本身。
关键词:
- 像素画
- NES 调色板
- WebSocket
- 流式推送
- 作品画廊
- pixel art
- AI 工具

---

上次写了[看 AI 画像素画](/看-ai-画像素画)，工具上线后自己用了一阵子，陆续改了三处。

第一个是颜色。原来模型直接输出 hex 色值，比如 #FF0000 画红色、#008751 画绿色。理论上很灵活，实际上模型经常犯错。它可能输出 #FF00O0（把 0 写成 O），或者挑一个肉眼几乎看不出差别的邻近色。更常见的情况是，模型在同一个画面里用了十几种颜色，失去像素画应有的克制。

问题的根源在于自由度太高。像素画的魅力恰恰来自限制——颜色越少，轮廓越清晰，风格越统一。让模型从几百万千色中自由选择，不如给它一个固定的调色板。

我选了 NES 16 色调色板。从 NES 的 32 色中挑出 16 个代表色，覆盖黑、白、灰、红、橙、黄、绿、蓝、紫、粉。每种颜色有一个短编号：c1 到 c16。模型只需要说「画 c6」而不是「画 #F83800」。

这个改动同时在提示词和代码两层落地。提示词里放了一张调色板对照表，列出色号、颜色名、hex 值和典型用途。代码层加了一个映射函数，把 c1-c16 转换成实际的 hex 值。前端完全不变，收到的仍然是 hex。

效果立竿见影。模型的输出干净了很多，颜色数量稳定在 4-8 种，不再出现「一种红色画出五个微妙变体」的情况。色号比 hex 好记，模型犯格式错误的概率也降低了。

第二个改造是通信方式。原来用 SSE（Server-Sent Events），前端通过 fetch 发起 POST 请求，服务端用 ReadableStream 逐行推送事件。SSE 的问题是单向的——客户端只能听，不能说。如果用户想中途停止，前端只能中断 fetch，服务端那边的 LLM 调用还在继续跑，资源白白浪费。

改成 WebSocket 后，客户端和服务端之间是一条双向通道。前端发一条 `{type: "start", image: "..."}` 消息启动绘制，服务端逐笔推送每个工具调用的结果。用户点停止时，前端直接 `ws.close()`，服务端立刻感知到连接断开，终止后续的 LLM 调用。

SSE 还有一个隐性问题：心跳。Caddy 反代对空闲连接有超时机制，如果 LLM 调用耗时较长（比如 10 秒以上），中间没有数据流动，Caddy 会断开连接。原来用 SSE 时，服务端每隔 3 秒发一个注释行做心跳保活。WebSocket 天然有 ping/pong 机制，不需要额外处理。

Bun 的 WebSocket 实现基于 uWebSockets，性能比 Node.js 的 ws 库高 7 倍左右。对于这个工具来说性能不是瓶颈（一次只有一个用户在画），但 API 设计更简洁。`server.upgrade(req)` 一行完成协议升级，`ws.send()` 发消息，`ws.close()` 关连接。

前端的改动也不大。原来是一段 SSE 解析逻辑，要按空行分割事件帧、过滤心跳行、提取 data 字段。改成 WebSocket 后，`ws.onmessage` 直接拿到完整的 JSON 对象，解析一次就行。

通信层改完之后，又做了一层优化：流式推送。原来 LLM 调用用的是 `stream: false`，服务端等模型生成完全部内容，才开始处理 tool call。改成 `stream: true` 后，服务端逐 token 接收 LLM 的 SSE delta 流，按 index 收集 tool_calls 碎片，拼出完整的 tool call 后立即执行并推送到前端。首帧延迟从「LLM 总耗时」降到「第一个 tool call 完成时」。

最后加了作品画廊。每次绘制结束后，前端把参考图、最终画布和完整操作序列（每一步的 paint / fill_rect）保存到服务端。画廊页面下方展示所有作品，点击可以回放绘制过程。操作序列的存在让「过程」本身成为作品的一部分，不只是结果截图。

三次改造指向同一个方向：减少模型需要做的无关决策。固定调色板让模型不用纠结选色，WebSocket 流式推送让前端不用纠结协议和延迟，画廊让作品过程可追溯。好的工具应该让使用者专注于创作本身，而不是跟工具搏斗。

工具地址：[AI 像素画](/projects/pixel-art/)

***

相关文章

- [看 AI 画像素画](/看-ai-画像素画/) — 像素画工具的初版实现
- [Harness Engineering 为什么AI犯错时该追问系统](/harness-engineering-为什么ai犯错时该追问系统/) — 系统层面思考 AI 犯错
- [用 Bun WebView 给博客搭了一个 Mermaid 渲染引擎](/用-bun-webview-给博客搭了一个-mermaid-渲染引擎/) — 另一个 Bun 实战项目
