从 Rust 到 canvas: 我为什么开始做图片工具
从 Rust 到 canvas
写 Rust 的这几年,我一直在系统层里。CUDA、PyTorch、内存布局、指针别名分析。这些东西的美在于它们完全可以被形式化 — 你写下的每一行都可以推导,都可以证明。这种确定性本身让人上瘾。
但过去半年,我开始花越来越多时间在一件跟这些完全无关的事情上:做一个浏览器里跑的图片审美工具。它没有 kernel、没有 SIMD、没有 lock-free 队列。它只是 HTML5 canvas,加几十个手写的 blend mode 和 color grade。
我做这件事的原因不是技术升级 — canvas 的技术栈比我平时接触的低几个数量级。是审美需求。
起因:一次晚上八点的散步
那天下午我拍了一张街边灯的照片。想发出去,用 iPhone 自带的编辑滑了半天。滤镜要么太”美颜”要么太”filter 感”,怎么调都不对。
我意识到问题不在照片,是在滤镜的组织方式。市面上几乎所有工具都是 “200 个 filter, 你自己挑”。这是最偷懒的 UX — 把”选择的痛苦”外包给了用户。
真正的解决方案不是加更多 filter,是减少到几个有明确 attitude 的 vibe。像 Pantone 只有几百种颜色但每种都有名字有情绪一样。
五个 Mojo Pack
于是有了 PixMojo 的核心 framework:
- Movie — 像一帧电影
- Clean — 像 Apple 广告
- Premium — 低调不张扬
- Dream — 像 Golden Hour
- Party — 让人停下来
每个 Pack 里 4-6 个 sub-look。不做 200 个 filter 大杂烩。做五个 attitude。这不是技术架构,是 taste 架构。
技术上反直觉的地方
作为写 Rust 的人,我第一反应是用 WebGL / WebGPU / WASM。但我最后选了纯 canvas 2D。
三个原因:
- iOS Safari 支持一致 — WebGPU 还没普及,WebGL 在某些 iPad 上有 driver 问题
- 调试体验 — canvas 2D 的
getImageData / putImageData让每个 pixel 操作可以 print,可以 breakpoint - 渲染逻辑就是 30 行 JS — 一个 warm cast + 高光提亮 + 暗部下压,写完就 30 行。上 GPU 之后你要写 vertex shader、texture upload、compile pipeline,代码量 10 倍。对这种”简单但需要 tuning”的场景,CPU 完胜
反直觉是:追求性能的极致时,选低层技术是对的;但追求 iteration 速度时,选高层反而是对的。
我在学的东西
从 Rust 转到这个项目,我在学的其实不是 canvas API。是如何让一个不需要用户学习的东西存在。
Rust 用户是拥抱学习曲线的 — 你花 3 个月学 ownership,那 3 个月是投资。
图片工具用户 不接受任何学习曲线。他打开、拖照片、看到结果、决定是否喜欢。整个流程要 30 秒内完成,不然他就走了。
这个约束彻底改了我的工程直觉。以前”这个 feature 我加个 config” 现在变成 “这个 feature 如果需要 config 就砍了”。以前”这里给用户一个选项” 现在变成 “这里我替用户决定”。
写 Rust 的思维是”给用户完全的控制”。做 taste brand 的思维是”给用户完全的信任”。
我以为我在换技术栈。其实我在换脑子。
我会继续在这里写这条路上遇到的东西 — 从 canvas 到 taste brand 到独立开发本身。如果你想看的话,pixmojo.app 是它当前的状态。