🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

手机渲染农场 lzlzlzhh/phone-render-farm

DeepSeek Harness
lzlzlzhh4 天前有提交已提供中文文档扫描:无法判定

用安卓手机浏览器把 Canvas 动画渲染成 PNG 帧和 MP4

29.8/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/11/2 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add lzlzlzhh/phone-render-farm
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:仅索引
是什么
dsh 原生插件 · browser
装得上吗
本站尚未做安装检查
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 4 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码
v2.7.0
⚪ 扫描:无法判定尚无人工评估
展开查看扫描说明与证据 ↓

已尝试扫描但取不到可分析的源码:仓库或包不存在、已删除,或为非 JS/TS 实现、未随包发布源码。无法判定,不代表安全。

  • · 包内无可扫 JS
  • · TS 源码(可能为非 JS 实现或未发布源码),无法判定
扫描于 2026/11/2(1 天前) · 范围 全包扫描(包内未声明入口) · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

用 Android 手机浏览器当渲染器,把 JavaScript Canvas 2D 动画渲染成 PNG 帧序列和 MP4。

README

由 DeepSeek 最新模型翻译生成
phone-render-farm 把 JavaScript Canvas 2D 动画渲染成 PNG 帧序列和 MP4 —— 用安卓手机的浏览器当渲染器。 智能体宿主机里不会启动任何浏览器。宿主机通过回环地址提供一个页面;手机浏览器加载它,在 上逐帧绘制,再把 PNG POST 回来。 agent host (container / VM / CI) Android phone ┌──────────────────────────┐ HTTP ┌────────────────────────────┐ │ farm_server.py │ ───────────▶ │ browser opens the page │ │ · work queue + 90s lease│ │ render.js draws frame i │ │ · frames/f00000.png │ ◀─────────── │ canvas.toDataURL() → POST │ └──────────────────────────┘ PNG back └────────────────────────────┘ └── ffmpeg: frames + audio ──▶ MP4 适用场景
展开完整文档(README 剩余 1 节)
phone-render-farm

把 JavaScript Canvas 2D 动画渲染成 PNG 帧序列和 MP4 —— 用安卓手机的浏览器当渲染器。

智能体宿主机里不会启动任何浏览器。宿主机通过回环地址提供一个页面;手机浏览器加载它,在 上逐帧绘制,再把 PNG POST 回来。

agent host (container / VM / CI) Android phone
┌──────────────────────────┐ HTTP ┌────────────────────────────┐
│ farm_server.py │ ───────────▶ │ browser opens the page │
│ · work queue + 90s lease│ │ render.js draws frame i │ │ · frames/f00000.png │ ◀─────────── │ canvas.toDataURL() → POST │ └──────────────────────────┘ PNG back └────────────────────────────┘ └── ffmpeg: frames + audio ──▶ MP4

适用场景

这个工具只针对一种部署方式:智能体跑在同一台安卓手机上的容器里(比如 proot/chroot 容器里的 DeepSeek Harness、Termux 等)。容器和手机共用回环网络命名空间,所以手机浏览器能直接访问容器的 127.0.0.1 端口。

┌─────────── Android phone ───────────┐
│ ┌─── container ───┐ ┌── browser ─┐│
│ │ agent + farm │ │ renders ││
│ │ 127.0.0.1:8098 │◀─│ frames ││
│ └─────────────────┘ └────────────┘│
└─────────────────────────────────────┘

DSHA 设备桥(如果存在)会自动拉起手机浏览器。没有它,就手动打开 URL —— 其余流程完全一样。

如果你的智能体跑在另一台机器上,需要加 --bind 0.0.0.0 --host <LAN-IP>,并且手机要在同一网络。farm.sh doctor 会检查这一点。

为什么做这个

有些智能体宿主机根本跑不了无头浏览器。这个项目是在一台跑在 Android 15/17 内核上的 Ubuntu 24.04 容器里做出来的,那里每次启动 Chromium 都以 SIGTRAP(退出码 133)告终 —— 容器自带的 Chromium 本身没问题(不缺任何库),但安卓内核不给它所需的进程/沙箱原语(NoNewPrivs: 1、Seccomp: 2)。

在那台宿主机上验证过的失败方案:Playwright、@puppeteer/browsers、apt 的 chromium-browser(一个 snap 壳)、headless shell、--single-process、--no-zygote、--disable-seccomp-filter-sandbox、--print-to-pdf、--dump-dom、Xvfb,以及会话 D-Bus。全部退出码 133。

手机浏览器没有这些问题 —— 它跑在安卓已经批准的进程上下文里。所以:别再启动浏览器了,借一个已经在跑的。

安装

已在同一台安卓手机的容器里运行的 DeepSeek Harness 上验证:

git clone https://github.com/lzlzlzhh/phone-render-farm ~/.agents/skills/phone-render-farm

或者直接用脚本 —— 只需要 POSIX shell 和 Python:

git clone https://github.com/lzlzlzhh/phone-render-farm && cd phone-render-farmbash
bash farm.sh doctor

脚本是通用的(bash + Python 标准库),也能配合其他智能体框架使用。目前只验证了 DSH 设备端,其他环境尚未测试。

环境要求: Python 3.8+(仅标准库,无需 pip 安装)、bash、一台 Android 设备(浏览器能访问宿主机的端口),以及 ffmpeg(仅当你要输出 MP4 时需要)。

手机上用哪个浏览器

Chrome 和 Microsoft Edge 都已在设备端验证可用(成功导入 three.js r186、渲染 3D 场景、捕获帧)。其他浏览器尚未测试。

如果渲染卡住——浏览器在反复加载页面后偶尔会进入异常状态。强制停止浏览器再重新打开 URL 即可恢复,帧会从断点继续。

快速开始 bash bash farm.sh doctor # can this device work?
bash farm.sh new /tmp/job 1920 1080 30 20 # 20s × 30fps = 600 frames
$EDITOR /tmp/job/render.js # define window.__render(ctx, i, t)
bash farm.sh start /tmp/job # detached server
bash farm.sh open /tmp/job # launch the phone's browser
bash farm.sh watch /tmp/job # poll until ✅
bash farm.sh assemble /tmp/job mix.wav # MP4 (omit audio for silent video)

有 DSHA 设备桥接时,open 会自动通过它启动默认浏览器;没有的话,它会打印 URL,由你自己打开。其余操作完全一样。

实测数据

在一加手机(Android 17,arm64 容器)上,1920×1080 PNG:

| 600 帧 | 20.2s — 29.8 fps |
| 3000 帧 | 37.8 fps |
| 回传吞吐 | 427 MB/s(10 MB 用时 0.02s)——从来不是瓶颈 |
| 帧完整性 | 3000/3000,零丢失,全部 1920×1080 |
| 同一任务改用容器内 skia-canvas | 600 帧用时 45.9s(13 fps) |

手机比在宿主机上用 Skia 进程内渲染快约 2.3 倍,因为浏览器在真正由 GPU 加速的 Skia 上做合成。

render.js 的约定 js
window.__render = function (ctx, i, t) { // t === i / fps
const W = ctx.canvas.width, H = ctx.canvas.height;
ctx.fillStyle = '#f4efe3'; ctx.fillRect(0, 0, W, H);
ctx.fillStyle = '#c23a1b';
ctx.fillRect(W / 2 - 100 * t / 4, H / 2 - 50, 200 * t / 4, 100);
};

三条规则,因为真正搞坏渲染的就是这三点:

- 确定性。 同一个 i 必须始终画出相同的像素。不要用 Math.random(),不要用 Date.now()——用 i 给 PRNG 播种(farm.sh new 自带 prnd() 辅助函数)。否则续跑的帧会不一致,接缝只在播放时才暴露。
- 自包含。 渲染农场每帧都会重置 transform 和 globalAlpha。自己设置样式,自己画背景,绝不依赖上一帧。
- 每帧控制在约 2s 以内。 更慢的帧会超出 90s 租约,被回收,然后永远重渲。

复用已有的 Canvas 动画,追加一个适配器即可: js
window.__render = (function () {
var render = null;
return function (ctx, i, t) {
if (!render) render = window.Anim(ctx.canvas);
render.render(t);
};
})();

中断后仍能继续

手机跑长时间渲染时,这一点最关键。

进度取决于磁盘上存在哪些帧文件,与内存状态无关。所以:

| 事件 | 会发生什么 |
| 手机锁屏、标签页被杀、来电 | 该页面停止认领。租约 90s 后过期,帧回到队列。重新运行 open —— 已完成的帧不会重渲 |
| 你改了 render.js 并重启服务器 | 服务器会嵌入一个 job token(render.js + 尺寸 + 总数 + 开始时间的哈希)。过期的浏览器标签页发现不匹配,会自动重载 —— 你不用碰手机 |
| 过期标签页仍试图提交 | 返回 409 拒绝。这条最重要:没有它,旧标签页会悄悄把旧动画的像素写进新任务,而帧计数器看起来一切正常 |
| 两台手机,或两个标签页 | 它们认领互不重叠的帧,并行渲染 |
| 你手动增删帧文件 | farm.sh resume <job> 重新同步 |

卡死检测在服务端;超过 STALL_LIMIT(默认 150s)后 watch 会带着恢复提示退出,而不是一直挂着。

适用范围 —— 这不是什么

仅限 Canvas。 像素来自 canvas.toDataURL()。手机浏览器没有 CDP 截图通道(getDisplayMedia 不存在,基于 chrome.debugger 的扩展也装不到 Android 浏览器上),所以 DOM 内容无法保真获取。

这意味着:

- ❌ Remotion / React DOM 渲染 —— Remotion 自己的渲染器会启动无头 Chrome,而这恰恰在这里行不通。需要 DOM 保真,就在真实浏览器可用的地方渲染,或通过 CDP 驱动远程浏览器。
- ❌ 网站自动化 —— 那是另一个问题,见 BrowserSkill(它的扩展需要 chrome.debugger,Android 上没有)。
- ✅ Canvas 2D 动画、数据可视化、信息图、生成艺术。
- ✅ 通过 --context webgl2 使用 WebGL2 —— 在 Adreno 830 / ANGLE(原生 GL 管线)上实测可用。three.js 在 Chrome 和 Edge 上都能跑。

WebGL:一个设置决定你能不能拿到像素

WebGL 在每次合成后会清空绘图缓冲区,所以除非创建上下文时带上 preserveDrawingBuffer: true,toDataURL() 只会返回背景。在手机上实测(同一场景、同一帧):

| 上下文选项 | 画布中心的 readPixels | 结果 |
| preserveDrawingBuffer: true | [194, 58, 27, 255] | ✅ 红色方块 —— 帧捕获成功 |
| 默认(false) | [244, 239, 227, 255] | ❌ 只有背景 —— 内容悄悄丢失 |

farm.sh start <job> --context webgl2 会替你设好。针对 GL 上下文写 window.__render(gl, i, t)(程序只编译一次,用上下文对象上的标志位守护;用 uniform 驱动动画)。
如果你用 three.js,需要自己创建渲染器并设置 preserveDrawingBuffer: true —— 这个标志按上下文生效,three.js 默认把它设为 false。

存储。 1080p PNG 约 270 KB/帧;600 帧约 160 MB。--format jpeg --quality 92 能把体积压到约 1/5,但会出现一些色带 —— 对类摄影内容没问题,纯色信息图就别用。

跨设备。 默认的 127.0.0.1 在主机和手机共享网络命名空间时可用(DSHA 容器就是这种情况)。如果手机在另一台机器上,用 --bind 0.0.0.0 --host <LAN-IP>,并确认端口可达。doctor 会告诉你属于哪种情况。

命令

| doctor | 环境自检:可达性、ffmpeg、setsid、可执行位(区分 noexec 挂载和缺少可执行位)、组件是否存在 |
| new <job> <w> <h> <fps> <sec> | 生成 job.conf + 带注释的 render.js + frames/ |
| start <job> [--format png\|jpeg\|webp] [--quality N] [--batch N] [--port N] [--bind A] [--host H] [--context 2d\|webgl2] | 启动后台服务 |
| open <job> | 让手机默认浏览器打开 worker 页面 |
| watch <job> | 轮询直到完成,带卡死检测 |
| status <job> | 一次性查看进度 / fps / eta / inflight / stall |
| resume <job> | 编辑帧文件后重新同步 |
| stop <job> | 中止并关闭;帧保留 |
| assemble <job> [audio] | 用 ffmpeg 合成 MP4(-crf 18 -pix_fmt yuv420p -movflags +faststart) |
| demo | 用假客户端离线检查协议 —— 完全不碰手机 |

信任它之前先验证

python3 farm_selftest.py # 36 assertions: queue, leases, resume, abort,
token rejection, legacy compat, page generation
bash farm.sh demo # end-to-end protocol without a phone

然后跑一个小规模真实任务(60 帧),亲眼看看返回的几帧,再决定要不要跑长时间渲染。

文件

| farm.sh | 命令行:脚手架、后台启动、浏览器分发、watch、assemble |
| farm_server.py | HTTP 服务、工作队列、租约、任务 token、页面模板。只用标准库 |
| farm_selftest.py | 36 条断言的协议测试;不需要手机 |
| SKILL.md | 面向智能体的技能说明 |

运行时状态放在 /tmp/phone-render-farm/<job>/(可用 FARM_RUNROOT 覆盖)。

许可证

MIT
预览分享卡片长什么样 ↓lzlzlzhh/phone-render-farm
分享 · DeepSeek Harness Hub
lzlzlzhh/phone-render-farm

用安卓手机浏览器把 Canvas 动画渲染成 PNG 帧和 MP4

这是你的仓库?认领条目

回到顶部