Claude wrote most of the code. The design decisions, the rejections and the taste are mine — that was the experiment.
A free, two-part replacement for Screen Studio:
- Recorder (
retake) — a tiny native macOS tool that records your screen without the cursor, logs every cursor movement and click, and can record your webcam + microphone at the same time. Everything lands in one.takefolder. - Editor (
retake-editor.html) — one HTML file that runs in Chrome. Nothing to install, no server, no account, works offline. Drop the.takefolder in, press Auto and it builds zoom-ins from your clicks, redraws a buttery-smooth cursor, adds the gradient framing / rounded corners / shadow, overlays your webcam bubble, and exports a polished MP4. It is one readable file, so you can check for yourself that nothing leaves your machine.
The editor also accepts any plain video (QuickTime recordings, OBS captures…) — without auto-zoom or the redrawn cursor, which need the recorder's data.
The recorder is macOS only. The editor is a plain HTML file, so it runs anywhere a browser does.
curl -fsSL https://raw.githubusercontent.com/tonhaoln/retake/main/install.sh | sh
That downloads the latest release, puts retake in /usr/local/bin and the
editor in ~/Applications/Retake, and stops. Read the script first
— it is short and there is no checksum step, so read it rather than trusting it.
The binary is not notarised by Apple. Two consequences worth knowing: a
tarball downloaded in a browser gets quarantined by macOS — the curl
line above does not — and macOS re-asks for permissions after each update.
Run the install command again — it downloads the latest release and replaces both files. Retake never checks for updates itself (no network code is the point), so updating is always your move. macOS will re-ask for permissions after an update; that's the signature changing, not a fault.
The first time you record, macOS will ask (or silently block) until you grant your terminal app (Terminal / iTerm / Warp) these, under System Settings → Privacy & Security:
- Screen Recording — required, for the capture itself
- Accessibility — recommended, for clicks (that's what powers auto-zoom) and the ⌃⎋ stop hotkey
- Camera / Microphone — only if you use
--webcam/--mic
After granting Screen Recording you must quit and reopen the terminal app once.
What Accessibility actually grants, said plainly: while a recording runs, macOS
hands Retake every click and keypress on the machine, not only the app you are
recording. Retake keeps click positions and the times of keypresses. It never
keeps which key, unless you pass --keys. Details in Privacy.
The agent path's programs live under agent/ (see "Let an agent make the
demo" below). One of them is a small driver that posts mouse and key events
so an agent can drive a demo with the real pointer. Posting needs Accessibility
for the app hosting the agent, and it is a separate program from the recorder
on purpose: the recorder only watches, and that stays true.
You need Apple's command line tools (free). If you don't have them:
xcode-select --install
Then build the recorder:
cd Recorder
swift build -c release
sudo mkdir -p /usr/local/bin
sudo cp .build/release/retake /usr/local/bin/
retake # main display + system audio
retake --webcam # + webcam bubble + mic
retake --mic # + mic voiceover, no webcam
retake --window safari # record one window (see --list for names)
retake --area 100,80,1280,720 # record a region — x, y, width, height from the top-left
retake --display 1 # record your second display
retake --list # show displays and windows
retake --keys # ALSO record which keys you press (see below)
retake --no-system-audio # skip app/system sound
retake --no-notify # skip the "Saved" notification
retake --fps 30 # lighter files
retake --out ~/Movies # save somewhere other than ~/Desktop/Retake
retake --json # for scripts and agents: JSON events on stdout (see below)
With --webcam or --mic, Retake switches the camera on first and waits for
you to press Enter. A camera takes about a second to wake up, and this way
that second happens before the recording rather than inside it — so your take
starts when you do. (Scripts aren't prompted.) Plain retake starts straight away.
Stop with Ctrl+C in the terminal — or ⌃⎋ (Control+Escape) from any app.
You get a folder like ~/Desktop/Retake/2026-07-29 14.03.12.take containing
the raw video (with system audio), cursor data, and webcam/mic track.
One thing about --window: don't move the window mid-recording — the cursor
is logged against where the window started.
If something goes wrong mid-recording — you close the terminal, the machine gives up, you force-quit — the take survives. Closing the terminal stops it cleanly; a hard crash costs you the last few seconds, not the whole session.
Privacy note: by default the recorder logs when keys are pressed but never which keys. Full detail in Privacy below.
Driving it from a script. retake record --json (or retake --json) keeps
stdout for machines: one line {"event":"recording","take":"…","width":…,"height":…,"fps":…}
when capture starts and one {"event":"saved","take":"…","droppedFrames":0} when it
stops (or {"event":"error",…} and a non-zero exit if the recording was cut short).
Everything human moves to stderr. Start it with stdin not a terminal and it
records at once (no Enter prompt); stop it with SIGINT or SIGTERM. record is
the explicit name of what plain retake does.
System audio is recorded by default; --no-system-audio turns it off. Worth
knowing if you record calls: the rules on recording other people vary by country
and, in Australia, by state.
- Open
retake-editor.htmlin Chrome (or Edge/Arc — anything Chromium). Tip: keep it in your Dock — it's just a file. - Drag the
.takefolder into the window. (Older.osrecrecordings open the same way, edits included.) - Press Auto in the Zoom section to build zooms from your clicks —
nothing is added until you ask. Then:
- Timeline: drag zoom blocks to move them, drag their edges to resize, double-click empty space to add one, ⌫ deletes the selected one.
- Selected zoom: drag the dashed ring on the preview to aim it, use the Level slider for intensity.
- Zoom timing: the Lead-in / Hold / Speed sliders control how early a zoom starts before each click, how long it lingers after, and how fast the camera moves. Adjusting them rebuilds the auto zooms live; hand-added zooms are left alone.
- Sidebar: background gradients or your own image; padding, corner radius,
shadow; cursor style (arrow / dot / halo / hidden), size, smoothing and click
ripples; hide-when-idle (the cursor fades out after a pause and returns the
instant it moves); motion blur on camera moves; keystroke display (if
recorded with
--keys); webcam corner, shape and size. - Audio: separate microphone and system-audio toggles + volumes, and optional click sounds mixed into the export.
- Crop: Frame → Crop… — drag a box (or snap to 16:9 / 4:3 / 1:1 / 9:16, or set a custom ratio like 21 : 9) to trim away the dock, menu bar, or anything else. Zooms and the cursor follow the crop automatically.
- Cut sections: press ✂ Split (or S) at the playhead, click a piece, press ⌫ to remove it. Click a hatched cut to select it and ⌫ restores it. Audio, zooms and click sounds all stay in sync across cuts.
- Trim: drag the ⟨ ⟩ brackets on the timeline — they snap to your splits and the playhead (hold ⌥ to drag free).
- Your look: "Make this my default style" makes your current settings the default for every recording that opens without edits of its own. The last three saved looks stack under the button — click one to bring it back as the default.
- Undo: ⌘Z / ⇧⌘Z walks timeline and crop edits back and forward (not while you're inside crop mode — Esc cancels that instead).
- Press ? for all keyboard shortcuts.
- The chip next to Export shows your settings (format · resolution · fps · ≈ size) — click it to change format (MP4/GIF), quality, resolution and frame rate. Hit Export; rendering runs in the browser at roughly real-time speed (measured: a five-second clip with zooms and motion blur exports in about six seconds at 1080p30 on an Apple-silicon MacBook) and the file downloads when done. (GIF ignores the resolution picker and renders at up to 960px / 15 fps — right for READMEs and PRs.)
Your edits are saved automatically (in the browser, per recording) — close the
tab, come back tomorrow, drop the same .take folder in, and everything is where
you left it.
retake render ~/Desktop/Retake/2026-07-29\ 14.03.12.take
retake render that.take --format gif
retake render that.take --res 1440 --fps 60 --quality high --out ~/Movies/demo.mp4
retake render that.take --json # one JSON line: file, size, zooms, seconds
retake render produces exactly what the editor's Export button would, with
no window open. It launches your installed Google Chrome headless with a
throwaway profile, opens the editor, loads the take, presses Export and
catches the file. It is the same compositor and the same codec path, so the
result is the button's result. It draws what the take's edits.json says
(next section); a take without one renders with the factory look and no
zooms. Nothing else is applied: no saved default look, no auto-zoom.
The file lands beside the take as <take name>.mp4 (or .gif), never
overwriting an existing one unless --out names it. The flags mirror the
export popover and default to the editor's defaults. Chrome is required (it
is also what the editor needs for H.264); --chrome PATH points at another
Chromium-based browser, --editor PATH at a retake-editor.html other than
the installed one. The five-second test clip renders in about seven seconds
including Chrome's start-up, measured on an Apple-silicon MacBook.
The editor also reads an edits.json sitting inside the .take folder, so
edits written by a tool, or copied over from another machine, open with the
recording in any browser. The editor does not write this file yet (it still
saves to the browser); when a take carries one, the newer copy wins, decided by
content rather than clocks, and if this browser held different edits the load
message offers them back rather than overwriting them.
The file is exactly what the editor autosaves, one JSON object:
{ "v": 2,
"set": { "pad": 7, "radius": 14, "bg": 2, "cursorStyle": "arrow", "crop": null, ... },
"segs": [ { "t0": 3.2, "t1": 5.4, "x": 640, "y": 380, "z": 2, "auto": true } ],
"trimIn": 0, "trimOut": 12.5,
"splits": [ 6.0 ],
"cuts": [ { "t0": 6.0, "t1": 7.5 } ] }Times are seconds in the source recording; x/y (zoom aim) and crop
are source-video pixels; z is the zoom factor. Values outside the recording
are clamped on load, and a damaged file costs only the edits, named in the load
message. set holds every sidebar control except two things: the export
format, size and frame rate (those live in the export popover and travel only
with a saved default look), and a custom background image (see Known
limitations).
Your look — background, padding, cursor, zoom timing, export format — lives in
the browser once you click "Make this my default style". Export look, next
to that button, downloads the look you are looking at right now (exactly what
Save would store) as a file, look.json, so tools outside the browser can use
it. The agent path below reads it from ~/.retake/look.json: the style keys
go into each take's edits.json set, and the export format, size and frame
rate go to retake render as flags.
The editor can't put the file there itself (a page may only download), so move
it once — and note the browser names a second export look (1).json, so move
the newest:
mkdir -p ~/.retake && mv ~/Downloads/look.json ~/.retake/look.jsonThe file is the saved-look blob: v, the style keys (never crop, cuts, zooms
or webcam placement — those belong to a recording, not to you) and the export
format, size and frame rate. agent/look.json in this repo is the factory look
with every key present; copy it and edit by hand if you'd rather not open the
editor. retake render never reads it — a render is a function of the take
alone.
Type one sentence into Claude Code — "record a ten-second demo of the checkout
on localhost:3000, my look" — and take your hands off the mouse. The agent
opens the page in a clean Chrome window, starts retake, drives the flow with
the real pointer (so the recorder sees real clicks and real cursor motion),
stops, writes edits.json in your look with zooms on your clicks (close
clicks share one) and the take trimmed to the flow (an agent records its own
thinking time before the first move; the trim is its decision, and the
editor's trim handles undo it), renders, and hands back the MP4 path. The take
is a normal take: open it in the editor to retake anything by hand. One caveat there: the
Auto button adds the editor's own zooms on top of the agent's rather than
replacing them, so delete the agent's first if you want Auto's set alone.
It runs from a clone of this repository, not from the installed files (the
install script puts only the recorder and the editor on your Mac). It needs
Google Chrome, the Xcode command line tools (swiftc, to build the small
pointer driver once), Node 22 or later, and Accessibility for the app that
hosts the agent (your terminal), because posting mouse and key events is what
that permission grants. Install the skill with one symlink from inside the
clone, then use it from any project:
ln -s "$PWD/agent" ~/.claude/skills/retake-demo
# in Claude Code, anywhere: /retake-demo http://localhost:3000 the checkout flowAny other agent can follow agent/SKILL.md as prose; the four programs it
calls (agent/mouse.swift, agent/chrome.mjs, agent/record.mjs,
agent/edits.mjs) are plain commands with --help.
What it touches, in full: the pointer and keyboard while it runs; a Chrome it
opens on a throwaway profile and quits at the end; edits.json inside the
take and the MP4 beside it; ~/.retake/mouse (the built driver) and, during a
run, ~/.retake/chrome-run.json and ~/.retake/record-run.json. The demo
Chrome is a normal Chrome loading the page you asked for; Retake's programs
open one socket to it, on this machine, and nothing else. When nobody has
named the page's selectors, the skill reads the page's HTML once with curl
before recording. It never records with --keys.
Screen Studio's polish comes from not baking the cursor into the recording. Retake does the same: the screen is captured cursor-free, the cursor path is recorded as data, and the editor re-renders a smoothed, resizable cursor on top — which is also why zooming stays tack-sharp on the pointer.
Named here so nobody has to discover them:
- Wide-gamut colour survives recording but clips at export. Takes are tagged with your display's real colour space (Display P3 on most modern Macs); the editor works in sRGB, so the small slice of colours outside sRGB is gently clipped in exports. Full-gamut export is on the list.
- Deleting one auto-zoom doesn't survive the timing sliders. While other auto-zooms remain, touching Lead-in or Hold rebuilds the whole auto set and the deleted one comes back. Deleting them all sticks, and zooms you added by hand are never touched.
- Custom background images aren't saved between sessions — re-pick after reopening. Gradients and every other setting persist.
- The editor still saves your edits and your default look to one browser's
storage. Edit in Chrome, reopen in Arc, and they won't follow you. A take
that carries an
edits.json(see Editing) opens with those edits anywhere, and Export look writes your look out as a file; writing edits back from the editor is next (see Roadmap). - The timeline is invisible to screen readers — though fully usable by pointer and keyboard shortcuts. The sidebar controls are ordinary accessible elements; the timeline itself isn't yet.
In the order they're planned — planned, not promised:
- Project file in the folder — the editor now reads
edits.jsonfrom the.takewhen it is there; writing it back from the editor (Chrome, via the folder picker) is the other half, so edits travel with the recording. - The agent path — shipped as a Claude Code skill over four small
programs under
agent/(see "Let an agent make the demo" above): the agent drives a page with the real pointer whileretakerecords, writesedits.jsonin your look, renders. None of it is needed for recording or editing. A thin MCP wrapper over the same commands follows if it earns one. - Menu-bar app, then Homebrew, then a signed DMG if the project earns it.
A screen recorder sees everything.
Nothing leaves your Mac. No analytics, no telemetry, no update pings, no accounts. The recorder writes files to a folder; the editor is an HTML file that runs offline. There is no network code in either half, and you don't have to take my word for it:
grep -rniE 'fetch\(|XMLHttpRequest|WebSocket|EventSource|URLSession|sendBeacon' \
retake-editor.html Recorder/Sources/retake/*.swift
Every hit is in Render.swift, and every one is the same thing: retake render opens one WebSocket to 127.0.0.1, to the DevTools port of a Chrome
it launched itself a moment earlier, and closes it when the file is written.
The recorder and the editor have no network code at all. The only http
URLs in the shipped editor are four comments crediting where code came from.
The agent path's Chrome helper (agent/chrome.mjs) opens the same kind of
socket for the same reason: one WebSocket to 127.0.0.1, to a Chrome it
launched itself on a throwaway profile, to read where things are on the page.
It quits that Chrome and deletes the profile when told to close.
Keystrokes are timings, not keys. By default cursor.json contains
exactly four things: cursor positions over time, click positions and times,
the times of keypresses rounded to the nearest 50ms, and a flag saying
whether click capture worked. The editor does not read those key times. The
agent path's edits script does, for one thing: to find where a demo's flow
ends (a flow that ends with typing) so the clip can be trimmed to it. Which
key you pressed is never recorded.
Granting Accessibility means the recorder sees every keypress on the machine
while it runs, including in apps you are not recording. It listens only, it
cannot alter or swallow what you type, and without --keys the key itself is
discarded the instant it arrives.
--keys is the explicit opt-in that also records the key labels, so the
editor can draw the on-screen keystroke overlay. Don't use it while typing
passwords. A default recording cannot contain your keystrokes.
If you would rather the recorder never watched the keyboard at all, say so in
an issue and a --no-input flag is a short patch away.
Found a security problem? Report it privately through the repo's Security tab rather than a public issue.
- "Could not listen for clicks" → grant Accessibility to your terminal, rerun. Recordings still work; you'd just add zooms manually. The ⌃⎋ stop hotkey needs the same permission — until then, stop with Ctrl+C.
- Black recording / permission error → grant Screen Recording, then fully quit and reopen the terminal app.
- Webcam file won't play in the editor → re-encode it, keeping the name:
ffmpeg -i webcam.mov -c:v libx264 fixed.mov && mv fixed.mov webcam.mov, then re-drop the folder. - Export has no audio → your browser can't encode audio (rare in Chrome on a Mac); update Chrome.
- 4K export refuses to start → try 1440p; some machines cap the hardware encoder.
The editor bundles two MIT-licensed libraries, inlined so the shipped file stays a single HTML document: mp4-muxer (c) Vanilagy, and gifenc (c) Matt DesLauriers. Full notices in NOTICE.
Retake is a personal tool, shared as-is: bug reports are welcome, and response times aren't promised.
Everything here is yours — MIT (LICENSE). Enjoy not paying a subscription.
