← All projects★ Featured · Active

SAM 2 from Scratch: Promptable Video Segmentation in the Browser

Segment Anything 2 is Meta's 2024 foundation model for promptable segmentation: click on an object in an image or a video frame and the model returns its mask, then follows that object through the rest of the video using a streaming memory of what it has already segmented. The paper reads cleanly, but a paper is not a model. I wanted to know whether I could rebuild the whole inference architecture from the paper and the released configuration, load Meta's published weights into my own code, and prove that what I had built was the same model, not merely a similar one.

RoleIndependent project
PeriodOctober 2026
StatusActive
StackML · Computer Vision · PyTorch · ONNX · WebGPU
0.0Max error vs Meta's package, every module87.7J&F on DAVIS 2017 val, Hiera-T81 MBWhole model in the browser

01 Overview

The reimplementation covers every inference-time module: the Hiera hierarchical vision transformer with windowed and global attention and query pooling between stages, the feature pyramid neck, the prompt encoder with its random Fourier position encoding, the two-way transformer mask decoder with its IoU and occlusion heads, the memory encoder that fuses a predicted mask back into image features, the memory attention that conditions each new frame on the prompted frames, the six most recent frames and up to sixteen object pointers, with two-dimensional rotary position embeddings, and the propagation loop that decides which memories a frame attends to. Training on the 50,900-video SA-V dataset took Meta 256 A100 GPUs for 108 hours; that part is not reproduced, and this page does not pretend otherwise.

Correctness is defined numerically rather than by eye. A dump tool records every intermediate tensor the official package produces on three test images and a 48-frame clip, including the exact inputs and outputs of the memory attention at five points in the video. Each of my modules keeps the parameter names of its checkpoint slice, loads it strictly, and is tested against those dumps. The result on both released sizes, Hiera-T and Hiera-S, is a maximum absolute error of exactly zero on every module and every propagated frame, forward and backward in time. Two details that the paper and configuration files do not state had to be recovered from the numbers: masks of clicked frames are binarised before they enter memory, and memory features are stored in bfloat16, which changes later frames by more than the test tolerance if skipped.

To check the model against a public benchmark I ran the standard semi-supervised protocol on DAVIS 2017 val, where every object is given its first-frame mask and tracked to the end of the clip. Hiera-T scores 87.7 J&F and Hiera-S 88.0. Meta's appendix reports 89.4 and 89.6 for the same architectures, but for models trained with additional internal data that were never released. On three of the sequences re-run with the official package and the public SAM 2.1 checkpoints, Meta's code matches mine to within 0.002 points, so whatever the gap is, it is not in the implementation.

The demo below runs the full model in your browser with ONNX Runtime Web, preferring WebGPU and falling back to WebAssembly. It runs at 512 pixels rather than the paper's 1024: on DAVIS that costs 5.6 J&F (82.1 against 87.7) and makes every frame about four times cheaper, which is the trade a browser demo has to make. Six graphs are exported from my implementation rather than Meta's: the image encoder, the prompt-and-mask head, the memory encoder, the memory attention, and two small object-pointer graphs. The 27-million-parameter encoder is the download problem. Standard dynamic int8 quantisation dropped mask agreement with the fp32 model to 0.93 IoU and uses operators that have no WebGPU kernels, so the shipped encoder uses weight-only int8 with plain dequantisation nodes, keeps the patch embedding and first two blocks in fp32, and agrees with fp32 at 0.99 IoU in 32.5 MB. The video loop was first rewritten in plain onnxruntime and numpy and shown to match the PyTorch predictor exactly, then ported to TypeScript and tested in Node against that loop frame by frame before it touched the page.

What is still open, in the order I would attack it: tracking several objects at once in the browser, which the PyTorch side already does by batching memories; box and mask prompts in video mode, which the exported head already accepts; a 1024-pixel video mode, which needs an int8 encoder that keeps parity on frames where two mask candidates are nearly tied, since the current one flips between them; and a small fine-tuning study of the mask decoder on a narrow domain to see how far the frozen encoder carries.

02 Architecture

IN YOUR BROWSER · ONNX RUNTIME WEB · WEBGPU OR WASMVideo frame512 × 512Hiera image encoderHIERA-T · INT8 WEIGHTSFPN neckSTRIDES 4 · 8 · 16PromptsPOINTS · BOX · MASKMemory bankSPATIAL MEMORIES · POINTERSMemory attentionCROSS-ATTENDS TO MEMORYPrompt encoderFOURIER POSITIONSSTRIDE 16STRIDE 4 · 8SKIPSMask decoderTWO-WAY TRANSFORMERMask · IoU · occlusion+ OBJECT POINTERCONDITIONEDMemory encoderMASK + FRAME FEATURESMASKNEXT FRAMESOBJECT POINTERAll six exported graphs run on your device: image encoder (Hiera + FPN), SAM head (prompt encoder + mask decoder),memory encoder, memory attention and two small object-pointer graphs. The loop repeats for every frame.

03 Results

Bar chart of DAVIS 2017 val J&F for this reimplementation next to the numbers in the SAM 2 paper
DAVIS 2017 val: this reimplementation on the public SAM 2.1 checkpoints next to the paper's numbers, which come from models trained with additional internal data

04 Live demo

Loading demo…

05 What was built

Reimplemented every inference module of SAM 2.1 in about 2,700 lines of typed PyTorch with no dependency on Meta's package, loading the released checkpoints strictly with every key consumed

Built a reference-dump and parity harness that pins each module to the official package's intermediate tensors; maximum absolute error 0.0 on Hiera-T and Hiera-S for images and for forward and reverse video propagation

Recovered two undocumented inference details from the numbers (binarised prompted-frame memories, bfloat16 memory storage) and documented the RoPE and layout choices needed for bit-exact results

Scored both sizes on DAVIS 2017 val with the official evaluator and showed the official package produces the same per-object scores on the public checkpoints; measured the cost of the 512-pixel setting the browser demo uses

Exported the model to ONNX at 1024 and 512 pixels, designed a WebGPU-compatible weight-only int8 scheme for the encoder after measuring three alternatives, and verified a pure-onnxruntime video loop against the PyTorch predictor

Wrote the browser runtime in TypeScript with a Node parity test against the Python loop, and built the interactive image and video demo on this page

The demo downloads 50 MB (image mode) or 81 MB (video mode) of weights on first use and runs them with WebGPU, or WebAssembly if WebGPU is unavailable. Nothing you upload leaves your device.