Get in touch
Gabs

Case study by Gabriel Espinheira, senior full-stack engineer

SnapForge, launch visuals without an upload

A free web app that puts a screenshot or a screen recording in a device or browser frame and exports a 4K image or a short video, all inside the browser tab. I built it directing coding agents: first commit 09/10/2026, version 0.2.0 on 11/10/2026.

Early days: version 0.2.0, with private source.

Three days, in numbers

7

device and window frames

12

templates that set up a whole scene

4,096

pixels on the longest side of a PNG

0

files uploaded to a server

142

tests across 29 files

24

commits from 09/10 to 11/10/2026

Counted from the repository on 12/10/2026, at version 0.2.0.

A product shot, without the upload

A launch needs its screens shown well: in a phone or a laptop, on a backdrop, sometimes moving. Shots makes mockups in the browser, and Screen Studio polishes screen recordings on the Mac. Both are good, and both set the bar I aimed for.

SnapForge puts the two jobs in one free web app and keeps every file on the computer it came from, because a launch screenshot usually shows work nobody has seen yet. Mockups come first and work today; editing recordings in the browser and a macOS recorder are planned. Three promises shape every feature:

Free
no account, nothing to install, every feature in the browser
Private
no rendering server: files are decoded, rendered and exported inside the tab, and drafts stay in the browser's own storage
Exact
an image comes out at the size you set, up to 4096 pixels a side, with a transparent background when you want one

Everything a product shot needs

Frames, templates, backdrops, motion and export, in one editor. Each picture below is a panel from the live editor beside an export it made.

Two SnapForge exports: a sample dashboard on a MacBook with a phone app on an iPhone in front of it, and the same dashboard on a blue iMac.
01

Devices rendered in 3D

The iPhone, iPad, MacBook and iMac render from 3D models through Three.js, lit by a soft product studio built in code, and the Pixel is an original model. Images and video play on the screen itself. The screen keeps up to twice the export's detail, with anisotropic filtering, so text on a tilted phone stays sharp.

The template gallery in the SnapForge editor, beside the Trio export: three iPhones in a fan on a blue wallpaper.
02

Twelve templates, one undo

A template sets the devices, their finishes, the layout and the backdrop in one step, and one undo takes all of it back. From there every part can change: up to three images or videos in a scene, each in its own frame, placed by dragging or by a layout.

The Background panel with its wallpaper grid, beside the Neon export: an iPhone turned isometric on a purple cosmic wallpaper.
03

Backdrops for any mood

Gradients, solid colours, a blur of your own media, wallpapers, your own image or a photo from Unsplash. Unsplash search goes through a small server adapter that keeps the access key off the page; every other backdrop works without it.

The Animate your mockup panel with the None, Zoom, Orbit, Float and Parallax presets, beside the Orbit export: a tilted iPhone between a sphere and a ring.
04

Motion, encoded in the tab

Zoom, orbit, float and parallax run on one clock that drives both the preview and the export. Mediabunny encodes the frames to MP4 or WebM through the browser's own WebCodecs, up to 1080p at 30 frames a second and twelve seconds long.

The Export settings panel set to 1600 by 1200 with a transparent PNG, beside the Pixel duo export: two Pixel phones on a teal gradient.
05

Exports at the exact size

A PNG comes out at exactly the size you set, up to 4096 pixels a side in landscape or portrait, transparent if you like. Sizes save as export profiles, a look saves as a style preset you can share as a file, and a PNG can go straight to the clipboard.

Every image here came out of SnapForge

Two sample screenshots, a dashboard and a phone app, through each of the twelve templates. Every image is a straight export at 1600 × 1200.

A sample analytics dashboard on a MacBook, with a sample phone app on an iPhone in front of it, on a pale abstract wallpaper.
Hero pairMacBook and iPhone, abstract wallpaper
A sample phone app on a silver iPhone, centred on a peach and blue abstract wallpaper.
PasteliPhone, abstract wallpaper
A sample phone app on a black iPhone turned isometric, on a purple and magenta cosmic wallpaper.
NeoniPhone, isometric, cosmic wallpaper
A sample analytics dashboard in a light browser window, on a violet gradient.
BrowserBrowser window, gradient
Three iPhones in a fan, each showing the same sample phone app, on a blue wallpaper.
TrioThree iPhones in a fan, wallpaper
A sample analytics dashboard on a dark MacBook, on a blue desktop wallpaper.
WorkspaceMacBook, desktop wallpaper
A sample analytics dashboard in a plain screenshot frame with a soft shadow, on an off-white background.
PaperScreenshot frame, solid paper colour
A sample analytics dashboard on a blue iMac, on a pale blue desktop wallpaper.
StudioiMac, desktop wallpaper
A sample analytics dashboard on a landscape iPad leaning to the right, on an orange desktop wallpaper.
TabletiPad in landscape, leaning right
The same sample phone app on two Pixel phones side by side, one frost and one pink, on a teal gradient.
Pixel duoTwo Pixel phones, gradient
A sample phone app on a tilted iPhone between a glossy sphere and a ring, on a violet gradient.
OrbitiPhone, sphere and ring shapes
A sample phone app on a black iPhone, tilted left, with the shadow of a window falling across a stone-grey background.
NoiriPhone, window shadow overlay

One scene, one renderer

A project is two things kept apart: the original files, stored as imported in the browser's IndexedDB, and a small scene that only refers to them by id. The scene holds the backdrop, up to three framed media, their styles and motion, and the output size, and it is all a draft needs to come back after a reload.

One Canvas 2D renderer draws the preview, the template thumbnails, the PNG and every video frame from the same scene at the same moment, so an export can't drift from what was on screen. Tilt goes through one WebGL quad with perspective-correct texture coordinates, and devices render through Three.js into the same frame.

The preview and the export are the same code.
  1. 01ImportDrop, paste or browse: PNG, JPEG and WebP, MP4 and MOV.
  2. 02Original bytesStored as imported in IndexedDB, apart from the project.
  3. 03SceneA small versioned document that references the media by id.
  4. 04One rendererCanvas 2D, with WebGL for tilt and Three.js for devices.
  5. 05Preview and exportThe same scene and the same time: preview, PNG, MP4 and WebM.

Six decisions, with their reasons

Most of them protect one thing: the files you bring in. The rest keep an edit reversible and an export honest.

Originals stay untouched

Imported files are stored byte for byte and never rewritten. The scene only points at them, so a crop or a frame change can always be undone, and media no project uses any more is deleted instead of filling the browser's storage.

One gesture, one undo

History keeps whole scene snapshots with asset ids, never the media. A drag is one step however many frames it lasted, and a template that changes devices, layout and backdrop is one step too.

Every load is validated

The scene schema bounds every number, colour and id, and a draft is checked against it each time it opens. Projects saved by older versions are migrated in memory, with their media left as it was.

Video is checked before download

An encoder gets fifteen seconds before the export stops with a clear error, and the frame timestamps are checked after encoding. A browser that drops frames gets a message, never a broken file.

Style files without your screens

A style preset is a small file anyone can import into another project. It carries the look, and media only when you choose: an early version leaked the screenshot behind a blur backdrop into the file, and the fix is covered by a test.

Analytics only after yes

PostHog loads after you accept and records page views only. Unsplash search sends the words you type and never your files, ten requests an hour per visitor, through fixed hosts with no redirects and an eight-second timeout.

Checked in two browser engines

Each feature was run in real Chromium and WebKit, not only in unit tests: three imports, a reload that restores the draft, and exact 3840 × 2160 and 2160 × 3840 PNGs in both engines. Exported MP4 and WebM files were decoded back to thirty frames over one second at 1920 × 1080. Where WebKit failed, the repository says so and the editor refuses the export.

A hard call: the browser first, the recorder later

Problem

The polish people know from Screen Studio, zooms that follow your clicks and a cursor that glides, needs a recorder that logs every click and cursor position as it records. That means a native macOS app with Accessibility and Input Monitoring permissions, and the first plans put that desktop app ahead of the editor.

Options

Build the Electron recorder first, and nothing is usable until capture, permissions and the editor all work. Or start in the browser, which can already decode, render and encode video, and add the recorder once the editor is worth recording into.

Choice

The website is the product, and nothing it does needs Electron. The scene and the renderer are shared code a desktop app can reuse later, with its recordings and click data arriving through the same local storage.

Cost

An imported recording carries no click or cursor data, so automatic zooms can't exist yet. Video export stops at 1080p, 30 frames a second and twelve seconds, and it depends on each browser's encoders: Chromium passes MP4 and WebM, and WebKit only passes simple WebM scenes, so SnapForge rejects the rest with an error.

A smaller call: the Pixel, by measurement

Google publishes no 3D model of the Pixel 11, so its geometry is built in code from the published specifications, 152.8 × 72 × 8.6 mm with a 6.3-inch 1080 × 2424 display, and its bezels, camera visor, punch-hole and buttons are measured from product renders. It's an approximation, and the repository says so.

Three days, a plan per feature

The first commit was on 09/10/2026 and version 0.2.0 shipped on 11/10/2026: 24 commits, all mine, and the work happened in agent sessions. My local logs hold 8 Claude Code and 4 Codex sessions on the repository.

Each feature started as a plan in the repository, fourteen of them, with the checks that define done. The verification record lists what was proven in a real browser and what wasn't, such as live Unsplash search, which waits on an access key of my own.

It is early. Recording and editing video are planned, not built, and the source is private. The marketing site, legal pages and quality gates came from Fabricator, the factory my studio scaffolds its websites with.