pa_encoder
v0.3.9
Published
HTML canvas frame encoder made for personal use.
Readme
HTML canvas frame encoder made for personal use.
Installation
npm install pa_encoderProvides:
- JavaScript API (ES module)
- CLI tool (
pa_encoder)
Usage
CLI (recommended)
Starts a local proxy server and opens a browser UI.
pa_encoder --url http://localhost:5173 --entry /src/main.jsOptions
--urlTarget site URL--entryEntry script imported in preview iframe--portUI server port (default:8787)
The UI shows a live preview, capture controls, and progress logs.
Live mode UI tips:
Duration=0means manual stopPendingCaplimits concurrent canvas snapshot tasksStats(ms)controls status update interval to keep UI lightweight
Frame mode UI tips:
Best/FSwrites PNG frames directly to a chosen directory from the UIStartchooses the first timeline frame to export;Framescontrols how many frames to exportWorkerscontrols parallel PNG encoding after each frame snapshot is capturedPendingBmplimits how manyImageBitmapsnapshots can be held in memoryWait=autouses a sketch render hook, exposed WebGPU queue, or tracked GPU context when available- WebGPU canvases opened inside the preview are configured with
COPY_SRC; during frame capture, submitted textures are copied to a readback buffer before the browser presents the canvas - WebGL contexts opened inside the preview are captured with
preserveDrawingBuffer: true - WebGL frame snapshots are read from the framebuffer with
gl.readPixels()in frame mode - Fully transparent snapshots are treated as not-ready captures and retried before export
For WebGPU sketches, expose either:
window.__pa_encoder_gpuDevice = device;
// or
window.__pa_encoder_waitForFrame = async () => {
await device.queue.onSubmittedWorkDone();
};JavaScript API
Live capture (real-time)
import { startLiveCapture, createZipExporter } from "pa_encoder";
const canvas = document.querySelector("canvas");
const exporter = await createZipExporter({ zipName: "frames.zip" });
const session = await startLiveCapture({
canvas,
exporter,
fps: 30,
maxPendingCaptures: 2,
});
await session.stop();startLiveCapture() returns { stop, stats, done }:
stop()finalizes capture/exportstatsis the live mutable stats objectdoneresolves when capture fully stops/finalizes
Deterministic frame capture (virtual time)
import {
virtualTimeCaptureFromStart,
waitForFrameReady,
createFramePngPipeline,
createFsExporter,
} from "pa_encoder";
const exporter = await createFsExporter({ dirNameHint: "frames" });
const pipeline = await createFramePngPipeline({
exporter,
encodeWorkers: 3,
maxPendingBitmaps: 4,
maxEncodeQueue: 8,
});
await virtualTimeCaptureFromStart({
fps: 60,
frameCount: 300,
start: async () => {
await import("/src/main.js");
},
onFrame: async (canvas, i) => {
await waitForFrameReady({ canvas, frameIndex: i, mode: "auto" });
await pipeline.capture(canvas, i);
},
});
await pipeline.drain();
await exporter.finalize();Hooks requestAnimationFrame, time APIs, and timers to ensure deterministic output.
Frame snapshots are captured sequentially for correctness, then PNG encoding and writes are pipelined.
Exporters
All exporters share the same interface:
{
write(frameIndex, blob);
finalize();
}ZIP exporter
import { createZipExporter } from "pa_encoder";
await createZipExporter({ zipName: "frames.zip" });Downloads a ZIP of PNG frames.
File System exporter (Chromium)
import { createFsExporter } from "pa_encoder";
await createFsExporter({ dirNameHint: "frames" });Writes directly to a directory (secure context required).
Best exporter
import { createBestExporter } from "pa_encoder";
await createBestExporter({ prefer: "fs" });Uses File System export when available, otherwise ZIP.
Supported Environments
- Modern Chromium-based browsers recommended
- Requires:
- ES module Web Workers
OffscreenCanvascreateImageBitmap
- ZIP export works in most modern browsers
- File System export requires Chromium + secure context
