splittools

field notes · March 12, 2026 · 3 min read

The Browser APIs Behind split.tools: How In-Tab File Splitting Works

A technical tour of the web platform features that make serverless file splitting possible: File API, canvas, Web Audio, MediaRecorder, and client-side ZIP.

split.tools has no backend for file processing — not a small one, none. Every split happens inside the browser tab. This post is the technical tour of how, for the curious and for anyone building similar tools.

Getting the file in: File API

A drag-and-drop or file-picker interaction hands the page a File object — a handle to bytes on disk, readable via arrayBuffer() without any network transfer. The OS-level open dialog is the security boundary: pages read only what you explicitly hand them. From here, each media type takes its own path.

Images: canvas

An image draws into an off-screen <canvas>, and drawImage() with source-rectangle arguments does the actual splitting — each grid cell is one draw call copying the exact pixel region into a tile-sized canvas. canvas.toBlob() then encodes PNG or JPEG using the browser's native encoder. Cut coordinates are computed against the image's true pixel dimensions (naturalWidth), so the on-screen preview scale never affects output precision.

PDFs: two libraries, zero servers

PDF needs real parsing, which is where compiled libraries running in the page come in. pdf.js — the same engine Firefox uses for its PDF viewer — renders page thumbnails onto canvases in a Web Worker, off the main thread. For the actual splitting, pdf-lib loads the document's object tree and copies selected page objects into a new document — pages aren't re-rendered, so fonts, vectors, and links pass through byte-faithful. Rebuilding the cross-reference table and writing the new file takes milliseconds.

Audio: Web Audio + hand-rolled WAV

AudioContext.decodeAudioData() turns any format the browser can play — MP3, M4A, OGG, WAV — into an AudioBuffer of raw Float32 samples. Everything downstream is arithmetic on those arrays: waveform peaks (max amplitude per pixel bucket), silence detection (RMS over 20 ms windows against a threshold), and cutting (index math — sample t × sampleRate marks the boundary). Export is a WAV encoder in ~60 lines: WAV is just a 44-byte RIFF header followed by interleaved 16-bit PCM samples. No encoding library required.

Video: captureStream + MediaRecorder

Video is the heaviest case. The splitter plays your marked segment on a <video> element while captureStream() exposes its output as a live MediaStream, and MediaRecorder encodes that stream to WebM using the browser's hardware-accelerated encoder. Frame-accurate cuts, no 30 MB WASM codec download — with the honest trade-off that capture runs in real time and re-encodes (why that's fine for social clips).

Getting files out: Blob URLs and a tiny ZIP

Results become Blobs, and URL.createObjectURL() gives each a temporary in-memory URL that a programmatic <a download> click saves to disk. Multi-file results are packaged by a ~100-line ZIP writer using STORE (no compression) — the slices are already-compressed formats, so deflate would spend CPU to save nothing. A ZIP is just local file headers, a central directory, and CRC-32 checksums; well within hand-rolling territory.

Why this architecture wins

No upload latency (a 2 GB file "loads" instantly), no server costs to claw back via subscriptions and watermarks, and privacy by architecture instead of policy. The web platform quietly became a capable media-processing runtime; split.tools is what building on that looks like. Try the result: image · pdf · audio · video.

keep reading