7 月 30 日,Reddit 的 r/ClaudeAI 板块出现一段 15 秒的演示视频:一台手机对着笔记本电脑屏幕,一个文件就从闪烁的二维码里“长”了出来。帖子几小时内冲上板块首页,拿到 2.8K 赞、221 条评论。紧接着是互联网最熟悉的反应:这早就有人做过了。
两种反应都对,也都没说到点子上。做过类似点子的项目确实不少,README 里作者自己列了三个:2018 年用 Go 写的 txqr、放弃 QR 改用自定义彩色编码的 libcimbar,还有公开演示之后才发现“撞车”的 airgapped-qr-code-transfer。但本帖想聊的不是谁第一个想到,而是这套 2002 年就发表了的数学,为什么到 2026 年才真正好用。

decimen-optical-transfer,截至 2026 年 10 月 18 日拿到 6.9k Stars、828 Forks,TypeScript 写的,仓库 7 月 30 日创建,到现在发了 8 个 release。发送端把文件编码成一串永不停歇的二维码动画,接收端拿摄像头对准屏幕,收齐足够多的帧就把文件剥出来。不装 App、不配对,除了摄像头不要任何权限,两台设备之间不存在任何网络通路。
这篇文章想讲明白三件事:喷泉码在这条链路上到底解决了什么,README 没写的工程细节较真到什么程度,以及那个 418.5 KB/s 的纪录能不能代表你家里的使用场景?
几个真正打动我的设计
屏幕到摄像头这条链路有个天生缺陷:没有回传信道。接收端没法说“第三帧没看清,重发一次”,而摄像头必然丢帧,对焦抽风、手抖、屏幕亮度不足,随便哪一样都能让一帧作废。笨办法是把文件切成有序块循环播放,丢一帧就等下一圈,圈长几十帧的时候,这种等待足以让人想放弃。
decimen 的选择是喷泉码。发送端永远不直接发原始块,每一帧都是若干个块的异或组合,具体异或哪些块由帧序号确定性推导,子集大小按稳健孤子分布抽取。接收端收齐任意约 K×1.15 个不同的帧,顺序不限,就能把文件剥离出来。丢帧只损失时间,不损失正确性,两边帧率不一致也无所谓。

这张图里值得停三秒的是采集和解码那两步。摄像头采集和解码跑在两个独立的 worker 池里,忙不过来的 worker 直接丢帧,喷泉码在数学上保证这种丢帧无害。这不是优化,是把硬件的不可靠当成了设计前提:摄像头一定会抽风,那就让抽风的代价只是多等几秒。
第二个打动我的地方是帧格式的自描述设计。22 字节的帧头塞进了线格式版本、特性标志位、会话 ID、序号、块数、块长、总长度和载荷哈希,两个魔法字节 0xD1 0xC3 打头,先回答“这是不是 Decimen 的帧”,再谈版本。没有握手,接收端可以在传输中途锁定码流,发送端重启换个会话 ID,接收端自动重置。版本字节的较真程度也值得说:协议文档写得很直白,遇到不认识的版本要明确告诉用户去更新设备,不许黑屏;flags 分成必须理解和可忽略两半,以后加特性只消耗一个 bit,不用再打破一次兼容。
0 u8 magic 0xD1 两个魔法字节合起来回答“这是 Decimen 的帧”
1 u8 magic 0xC3 自 wire format v3 起固定不变
2 u8 version 3,遇到不认识的版本要明说,不许黑屏
3 u8 flags 0x0F 必须理解,0xF0 可安全忽略
4 u16 sessionId 会话标识,发送端重启即更换
6 u32 seq 帧序号,同时是喷泉码的随机种子
10 u16 k 文件块数
12 u16 blockLen 块长
14 u32 totalLen 总长度
18 u32 payloadFnv 载荷哈希
代码组织透着同样的脾气。三个页面各自只是一个 TypeScript 模块,把 DOM 接到共享核心上,没有框架,没有状态库。共享层里 fountain.ts、protocol.ts、qr-raster.ts 各管一段,构建插件目录里每个文件只做一次精确的字符串手术,匹配不上直接抛错,让标记漂移在构建期爆炸,而不是把坏产物发上线。

架构图上有一条边想特别指出:发送端的二维码生成只有一条路径,实时流和动画导出共用 send/qr-frame.ts。导出功能是后来加的,共用同一条生成路径,意味着这两者不可能各自漂移。这种“让错误不可能发生”的结构,比任何单元测试都便宜。还有一个细节能说明作者真的踩过坑:fountain.ts 里有一个手写的确定性对数实现,JS 引擎对 Math.log 的实现有差异,两端孤子分布只要差一个字节,剥离开关就会静默失败,于是它用精确规定的 IEEE-754 运算自己实现了一遍 log。不过较真归较真,落到你手里好不好用,得跑起来才算数。
从零到传通一个文件
最省事的路子根本不用装。decimen.app 在两边设备上打开就能用,首次访问之后离线可用,想放桌面的话 iOS 和 Android 都能装成 PWA。发送端选文件或者粘一段文本,接收端授予摄像头权限对准屏幕,剩下的就是等进度条。文件名和媒体类型会保留,接收端先做 SHA-256 校验,校验通过之前什么都不给你存。
想自己跑或者改着玩,拉仓库就行。项目是标准的 Vite 加 TypeScript,没有原生依赖,装完两条命令就能进开发服务器,热更新默认开着:
npm install
npm run dev
发送端开 https://localhost:5173/send/,终端会打印一个 Network 地址,接收端手机在同一个局域网打开它,自签名证书接受一次即可。想测吞吐可以走基准命令,它会把发送端锁定在标准的 1 MB 载荷上,跑完把完整诊断报告存成 JSON:
npm run benchmark
这套基准工具是我喜欢这个项目的另一个原因。它不是嘴上说“每秒几百 KB”,而是把每一次运行的完整报告落盘,你想宣称纪录,得用 promote 子命令提名,README 的速度表由 benchmarks/records.json 渲染生成。每条纪录都能点开看当时的完整诊断,这种“欢迎来杠”的姿态在个人项目里不多见。目前表上挂着两条:桌面到手机 418.5 KB/s 持续、601.5 KB/s 峰值,设备是 49 寸 Odyssey G9 接 iPhone 17 Pro Max,1 MB 文件 2.5 秒传完;手机到手机 199.2 KB/s 持续、340.8 KB/s 峰值,同样的载荷花了 5.1 秒,两条都测于 2026 年 8 月 9 日的 v0.4.0。

纪录是纪录,你手里的设备是另一回事。第三方拆解视频拿默认配置实测,笔记本到手机只有 3 KB/s,跟纪录差了俩数量级,差别全在硬件和参数上:49 寸 OLED 和臂长距离的普通显示器,不是同一个世界。几个 Issue 里冒出来的坑,提前说清楚:
- 独立 HTML 版本收不了文件。build:standalone 产出的单文件在浏览器里拿不到摄像头权限,气隙环境想用得自己起服务,这是 Issue #64 的结论。
- VA 面板和高帧率会打架。Issue #49 报告 60 FPS 下 VA 显示器传输速率暴跌,液晶灰阶响应慢会把帧糊成帧间混合。
- 默认参数不是为你的显示器调的。60 FPS 推 2953 字节的配置在 49 寸 OLED 上是纪录,搬到普通显示器上,第三方给出的解法是降到 1465 字节、24 FPS。
不过参数这个坑摸清之后,剩下的问题就只有一个:它到底适合谁,适合到什么程度?
什么时候用,什么时候别用
适合的场景大致有四类,各自的优势和局限列成一张表,看得比说得清楚:
| 场景 | 典型用户 | 优势 | 局限 |
|---|---|---|---|
| 气隙设备打补丁 | 工控、涉密运维 | 数据路径只有可见光,不碰网络 | 带宽和加密都要另想办法 |
| 嵌入式设备取日志 | 产线工程师 | 有摄像头和屏幕就能收,不用开 USB | 摄像头分辨率得够拍清 QR 模块 |
| 无网络工地野外 | 现场技术人员 | 两台笔记本隔空对传,不用路由器 | 亮度、眩光、抖动都是失败模式 |
| 预录制投放 | 培训、实验室教学 | 导出 APNG 动画,任何摄像头对着放都能收 | 只能单向,没法互动 |
不适用的也说两条。你只是想给朋友发个视频,AirDrop 和数据线是更合适的替代方案,光学传输的天花板是屏幕刷新率和摄像头帧率的乘积,2026 年 8 月的纪录是 418.5 KB/s,千兆网卡是它的两千多倍。你需要传输加密内容,这个项目帮不上忙,作者在 README 里写明了:两种模式都不加密,屏幕上有什么,任何对准它的摄像头就能读到什么,它给你的是无网络,不是机密性;文件超过 64 MB 同样别勉强,理论上限就在这里,实践中你会在等待里自己投降。
同类项目各选各的路。txqr 用 Go 实现了同样的喷泉码思路,设计文章写得极好,但要装运行时。libcimbar 放弃 QR,用自定义彩色编码换更高吞吐,代价是编解码器全都要自己实现。LiveDrop 往商业化和长焦镜头方向走,几百米距离是它的卖点。decimen 的位置是浏览器原生:打开网页就能用,二维码是标准码,解码器是裁剪过的 zxing-cpp 编译成 WASM。但项目值不值得长期跟,光看定位不够,还得看维护它的人。
社区怎么样,维护靠不靠谱
这个项目的社区账面上不算大,先把硬指标摆到桌面上,再逐条说我的判断和担忧:
| 指标 | 数据 | 说明 |
|---|---|---|
| Stars | 6,938(2026-10-18) | 两个半月从 0 涨起来,增速很陡 |
| 核心维护者 | 2 人 | 主维护者 27 次提交,第二贡献者 6 次,Bus Factor 约等于 1 |
| Open Issues | 29 | 三分之一是功能请求,真正的 bug 报告不多 |
| Forks | 828 | fork 与 star 比约 12%,想自己改的人不少 |
| 协议 | AGPL-3.0-or-later | v0.4.0 起从 MIT 改来,网络使用也需开源 |
Bus Factor 这件事必须直说。贡献者列表里只有 Evan Crawley 和第二贡献者 slavik0329 两个人,后者的 6 次提交集中在 v0.2.0 的任意文件传输功能。这种结构下,作者本人就是单点故障,他哪天没兴趣了,项目直接停摆。好在 Issue 区的响应速度还行,29 个 open issue 里大部分是本地化、界面和功能请求,作者基本都回过。
Reddit 那栋楼里最有价值的评论不是“这早就有了”,而是跟着冒出来的硬件报告:有人贴出笔记本到手机 3 KB/s 的实测,有人分析出 VA 面板灰阶响应和 60 FPS 推流会把帧糊成帧间混合。愿意把这类失败报告变成产品改动,比 star 数更让人放心。证据就在 release notes 里:v0.5.1 加相机选择器,理由写得很直白,实地报告显示自动选相机不可信,华为 P30 Pro 会把长焦当后置摄像头交给浏览器;发送端的字节数和帧率选项,同样来自这类报告。
安全方面有个早期事件值得记录。仓库的第一个 Issue 就是接收端内存耗尽:parseFrame 接受 totalLen 不一致的帧头,攻击者可以构造畸形头部让接收端疯狂分配内存。这个 Issue 有 6 条讨论,2026 年 10 月 9 日又开了 Issue #66,标题直指“拒绝不一致的帧长度”,看起来正是当年那个报告的收尾。
协议切换的代价也真实存在。v0.4.0 重写了喷泉码,v0.5.0 又换成 22 字节带头,两个版本都不跟 0.4.x 互通,release notes 里明确要求两端同时升级。对一个 2026 年 7 月才出生的项目,这是可以理解的早期迭代,但如果你打算在产线上用它,盯紧版本兼容表。说了这么多客观指标,该说说我自己的判断了。
我的真实看法
我一开始以为这又是一次“AI 一夜造出来”的玩具。它确实是 Claude Code 帮着写的,作者自己讲过项目源自一个缓存 MP3 播放器的副业。但翻开协议文档半小时之后,我的判断收窄了:孤子分布的确定性实现,golden vectors 测试向量,固定的 QR mask,还有锁定 L 级的纠错等级,这几样堆在一起,是一个真的在真实设备上对调过的人留的痕迹。
喷泉码是这个项目的灵魂,值得多说两句。顺序轮询方案丢一帧要等一整个循环,而 LT 码把“哪一帧有用”变成了统计问题:收齐 K×1.15 个不同帧就能剥,帧与帧之间没有依赖。对焦抽风、手抖、亮度不足,在这个模型里全部降级为多等几秒。单这一条,就足以把它和“二维码轮播文件”的同类项目区分开。
跟同类项目的位置差异,一张表就能看清楚,省得再用大段文字逐条描述半天:
| 项目 | 思路 | 状态 | 与 decimen 的差异 |
|---|---|---|---|
| txqr(2018,Go) | 动画 QR 加喷泉码 | 多年未更新 | 思路同源,要装运行时,无基准数据 |
| libcimbar | 自定义彩色高密度码 | 活跃 | 吞吐上限更高,编解码器全自研 |
| airgapped-qr-code-transfer | 顺序分块加压缩 | 个人项目 | 无喷泉码,丢帧即等待 |
| LiveDrop | 动态 QR 加长焦光学 | 商业化 | 距离和速度往专业方向走,不开箱即用 |
“这早就有人做过了”的 pile-on 搞错了一件事。Prior art 从来不稀缺,稀缺的是把 prior art 做到开箱即用。零安装,标准 QR,浏览器内 WASM 解码,可复现的基准收据,这四个条件同时满足的,表上只有它一个。txqr 2018 年就把喷泉码写明白了,但那之后的八年,没有一个浏览器原生的实现把这条路走通。
要挑刺的话有三处。49 寸 OLED 上的 418.5 KB/s 是真实纪录,但把它当成“你的使用体验”会失望,臂长距离的普通显示器上能到几十 KB/s 就算顺利。AGPL 是一堵墙,v0.4.0 从 MIT 切过来之后,想把它嵌进商业产品的人都要重新过法务,而作者没有解释这次切换的动机。
不过单维护者结构下,AGPL 反而给了社区一个兜底:最坏情况 fork 出走,协议保证分支可以合法存在。这把作者的个人风险和项目的存续风险切开了一点,算是这堵墙唯一的温柔面。
趋势上我看这个方向在变热。LiveDrop 在往商业长焦走,Issue 区里有人求 Cimbar 支持,有人要彩色 QR,还有人提议加音频回传信道,方向各不相同,但都指向同一件事:光学链路正在被当成一个正经的数据平面来对待。decimen 卡住的位置很准,不追求极限速度,追求任何有浏览器和摄像头的设备都能参与。该看的资源和链接放在下面,文档比 README 详尽得多。
资源地址
| 资源 | 地址 |
|---|---|
| GitHub | https://github.com/bashalarmistalt/decimen-optical-transfer |
| 在线体验 | https://decimen.app |
| 架构文档 | https://github.com/bashalarmistalt/decimen-optical-transfer/blob/main/docs/technical/architecture.md |
| 协议文档 | https://github.com/bashalarmistalt/decimen-optical-transfer/blob/main/docs/technical/protocol.md |
| 基准数据 | https://github.com/bashalarmistalt/decimen-optical-transfer/blob/main/benchmarks/records.json |
| 独立解码器 | https://github.com/bashalarmistalt/decimen-codec |
链接就这些,真正详尽的是 docs/technical 下的架构和协议文档。回到最开始的问题:这个项目到底值不值得占你收藏夹的一个位置?
值得试,但摆正它的位置
如果你手上有气隙设备要打补丁、有产线控制器要取日志,或者你就是想在两台没有任何网络的笔记本之间倒个文件,现在就打开 decimen.app 试一把,成本为零。从接收端入手,先拿 demo 模式练手,别一上来就传 64 MB。
如果你还在观望,盯两个指标:协议还改不改,贡献者名单 lengthen 不 lengthen。wire format 从 v2 到 v3 只用了四天,这种迭代速度对使用者是成本;两个人的贡献者名单决定了这个项目能走多远。这两点决定了它能不能从好用的个人工具变成可以依赖的东西。
一个文件从屏幕跳进摄像头,中间没有网络栈、没有握手、没有 TCP 重传,只有光和一套 2002 年就发表了的数学。这件事本身就很浪漫,剩下的都是工程。

