What's New
The rebuild release. Better Shot's capture pipeline, floating recording bar, and both editors were rebuilt from the ground up. Recording gained cursor-tracked auto-zoom, self-hosted share links, and a capture core that survives a crash mid-recording. The editors gained proper tabbed inspectors, and the parts of a screencast that used to need After Effects (speed ramps, transitions, scene changes, a tilted 3D card, color) now live in dedicated tabs.
Added
- Recording options in Settings: Settings > Recording now holds the camera and microphone pickers, the system audio toggle, a 0/1/3/5 second start delay, and the teleprompter switch. They read and write the same preferences the recording bar uses, so a choice made in either place shows up in both
- Cloud share compresses before it publishes: Sharing from either editor now runs the file through a local compression pass before a single byte is uploaded. Images are downscaled to 2560 px and encoded as both PNG and JPEG with the lighter file winning, and a shot with real transparency keeps its alpha. Videos are re-encoded to 1080p MP4. Whenever the compressed file does not come out smaller, the original uploads instead. Everything publishes straight to your own R2 bucket, configured in Settings > Sharing
- A cursor the editor draws: The pointer is no longer burned into the capture. Its path, its clicks and every shape it wore (arrow, beam, hand, resize) are saved beside the recording, so the editor draws it back and you restyle it after the fact. A spring chases the recorded path, so a shaky hand glides, the pointer never trails behind the motion, and a click lands exactly on the spot instead of arriving late. A Cursor tab holds the type (the pointer you had, or a soft touch ring), size, how far it leans into a fast sweep, whether it fades out once you stop moving, and the motion feel: Relaxed, Smooth, Natural or Snappy. The preview and the exported file draw the same cursor, scaled by the zoom like the rest of the picture
- Mute the mic mid-recording: The recording bar grew a microphone button with its own level under the glyph, so a mic that has gone quiet shows up while you record rather than in the finished file. Muting drops the audio instead of writing silence, so a muted stretch leaves nothing in the recording at all
- Keyboard overlay: Turn on "The keys I type" in Settings, Recording and BetterShot records your keystrokes beside the recording, then draws them under the video in the editor. Typing collapses into words instead of stuttering key by key, backspace takes the character back, and a shortcut like ⌘S gets its own moment. Every line stays editable or droppable in the Overlay tab, with position, size, weight, color, a backdrop plate and uppercase. Capture is off until you turn it on, asks for Input Monitoring, and never leaves your Mac
- Captions: The Overlay tab can transcribe the recording's own audio into timed caption lines, on device where your Mac supports it, so nothing is uploaded. Speech is grouped into readable lines that break on a pause, on a full stop, or when a line gets too long, and every line stays editable in the sidebar. Position, size, weight, color, width, a backdrop plate and uppercase, drawn on the canvas so captions stay readable while the recording zooms
- Text segments: Drop a line of text on the canvas for a stretch of the recording. It sits on the background rather than inside the video, so it holds still while the recording zooms under it. Size, weight, alignment, color, width and shadow, plus an entrance and an exit picked from fade, slide, pop or a typewriter reveal. Drag it where you want it on the preview, and one resolve drives both what you see and what the exporter draws
- Masks: An Overlay tab holds boxes that either hide part of the frame or spotlight it. Hide blurs or pixelates what is inside the box, and it is destroyed in the export rather than covered, so a password on screen never ships. Spotlight dims everything outside the box and fades in and out so it does not blink. Drag the box on the video, set its start and end from the playhead, and the same math paints the live preview and the exported file
- Text, blur and caption lanes: Text overlays, masks and caption lines now sit on the timeline under the zoom lane, one lane per kind, exactly like zoom cues. Drag a segment to move it, drag either edge to retime it, click it to select it in the sidebar. A lane only appears once it has something in it, so a plain recording still gets a plain timeline
- Color in the image editor: The screenshot editor gained the same Color section the recorder has, with the nine presets, an intensity slider and the full manual set (exposure, contrast, saturation, warmth, tint, fade, split tone, vignette, grain), so a set of screenshots and a clip can be graded to match
- Tabbed inspectors: The video sidebar is six tabs instead of one long scroll: Clip (scissors), Motion (zoom), Cursor (pointer), Camera (person), Overlay (masks) and Style (paintbrush). The screenshot sidebar got the same treatment with three: Annotate, Adjust and Style. Each tab holds only the controls that act on what you selected, so neither sidebar is a wall of disclosure triangles any more
- Per-clip audio mode: Speeding a clip up no longer forces one behavior on its audio. Each clip picks Mute, Keep Pitch, or Match Speed, so a 4x skim can stay silent while a 0.5x hold keeps a natural voice
- Transitions: Adjacent clips can cross into each other with Crossfade or Fade Through Black, with an adjustable length. The transition is baked into the export rather than faked in the preview
- Scene modes and split screen: Every clip carries its own scene: Both (screen with the floating face cam), Screen only, Camera only, or Split, which drops the bubble and gives the screen and the camera a pane each. Change scenes mid-recording and the export cuts between them
- 3D camera: A Perspective section in the Style tab tips the whole card in space. Drag the tilt pad for pitch and yaw, add Roll, and pull Depth for how hard the lens sells it. One pinhole projection drives both the live preview and the export, so combined tilts stay square and the drop shadow follows the pose
- Color correction: Exposure, contrast, saturation, temperature, tint, highlights, shadows, and vignette, with separate grades for the screen and the face cam
- Click highlights: Clicks recorded in the pointer sidecar now render as expanding rings in the export, derived from the same capture that drives auto-zoom
- Export progress: Exporting shows a live progress overlay instead of a frozen window, with the spring timing and reduced-motion fallback the rest of the app uses
- Countdown overlay: The start delay now draws a full-screen countdown so you know exactly when capture begins
- Escape closes editors: Escape dismisses the image and video editors, and an unsaved editor asks before it closes rather than discarding your work
- Split anywhere: One Split button, on click or with
S. Turn it on and click a clip where you want the cut; it turns itself off once the cut lands, so you never get stuck in a cutting mode.Deleteremoves the selected clip - Inspector tab shortcuts: Command 1 through Command 6 jump between the video tabs, and Command 1 through Command 3 between the screenshot tabs
- Annotation keys are visible: The image editor's tools always had single-key shortcuts, but nothing said so. Each tool now carries its key in the corner of its button and in its tooltip, read from the same table the keyboard uses, so the two cannot drift apart
- Auto-zoom (virtual camera): Recordings now track your cursor and zoom in around it, the way a screencast editor would. Pointer position is sampled at 30 Hz during capture and written to a
.pointer.jsonsidecar next to the video; the editor turns clicks into zoom cues, smooths them through a spring-damped viewport timeline, and bakes the motion into the export as per-frame transform ramps. Toggle it in the video editor's Zoom inspector, where each cue can be retimed, rescaled, or deleted, and preview it live before exporting - Zoom cue lane: A second timeline lane under the trim strip shows every zoom cue in place, so you can see where the camera moves relative to the footage
- Face cam: A circular camera bubble you can turn on from the recording bar and drag anywhere on screen. It is captured as part of the recording rather than composited afterwards, so what you position is exactly what lands in the video. Right-click the bubble's toolbar button for Small, Medium, and Large. Works with any connected camera, including Continuity Camera with your iPhone
- Clip timeline: The trim strip is now a multi-clip editor. Split at the playhead, drag either edge of a clip to trim it, click a clip to select and scrub it, and delete the parts you do not want. The player, zoom preview, and export all follow the edited timeline, so what you scrub is what you get
- Per-clip speed: Each clip carries its own speed between 0.25x and 4x, applied to video and audio together, so you can hold on a slow section and skim past a long one in the same recording
- Undo and redo in the video editor:
Cmd ZandCmd Shift Zstep through clip edits - Share links via Cloudflare R2: A Share button next to Export uploads the recording to your own R2 bucket and copies the public link to your clipboard. Configure it in Settings > Sharing with your account ID, bucket, public base URL, and an R2 API token scoped to Object Read & Write. Uploads go straight from the app to R2 over a hand-signed AWS SigV4 request (CryptoKit only, no SDK and no proxy worker), so nothing routes through a third-party service
- Keychain-backed credentials: The R2 access key ID and secret access key are stored in the macOS Keychain. Only non-secret config (account ID, bucket, public base URL, enable flag) lives in UserDefaults
- Test Connection button: Verifies R2 credentials before you rely on them, using an object-scoped probe so an Object Read & Write token passes without needing bucket list permission
- Source picker bar: Record no longer starts capturing the instant you click it. It opens a floating bar that lists every connected display and every open window alongside area selection, with microphone and system audio toggles and a 0/1/3/5 second start delay. Choosing a source morphs the same bar in place into the recording controls, so the bar you set up in is the bar you stop in
- Record a single window: Window capture uses a window-scoped content filter, so the recording follows that window and excludes whatever is stacked on top of it
- Recording area highlight: When recording a region, the area outside it dims and the edge gets a pulsing accent border, so you always know what is in frame. The overlay is click-through and excluded from the capture itself
- Redesigned recording bar: The floating bar now uses an
NSVisualEffectViewHUD material with refined corner radius and shadow, an AppKit-tracked hover puck that keeps responding while Better Shot is in the background, spring-based show and hide animations, and drag position that persists between recordings - Multi-display capture: Recording now targets the display under your pointer (or the frontmost window) instead of always grabbing the main display. Region capture carries its own
CGDirectDisplayID, so selections on secondary and negative-origin displays land in the right place - Crash-resilient recording: Video is written as fragmented MP4 with a 2-second fragment interval, so a crash or force quit mid-recording leaves a playable file instead of a corrupt one
- Microphone recording: Turning on the mic in the recording bar records your narration into the video as its own audio track, kept separate from system audio rather than mixed at capture time. Requires macOS 15, which is where ScreenCaptureKit added microphone capture, so the control is hidden on macOS 14
- Open editor after screenshot: New setting to jump straight into the editor after taking a screenshot (#72, thanks @y-u-s-u-f)
- New owl mascot: New app icon and logo across the app and website
Changed
- One toolbar, three buttons: Both editors now end in the same three actions. Save (Cmd+S) keeps your edits inside BetterShot and flashes a green Saved confirmation, Share uploads and copies a link, and Export, the highlighted button, writes the finished image or video to your Mac. The image editor's confusing extra buttons ("Save As" and the checkmark) are gone
- Local and Cloud tabs: The Screenshots and Recordings pages in Settings are now split into a Local tab for files on disk and a Cloud tab listing everything you have shared, with a thumbnail, a copy-link button, and a shortcut to the share page. The old dead Shared Links section is gone
- Same wallpapers everywhere: The image editor and the Recording Studio now share one wallpaper library with the same Recent and macOS tabs, so a screenshot and a clip can sit on the same background
- Share UI polish: The image editor's share button shows live progress with a cancel, the upload options popover was redesigned, and the share link panel now brings the app forward properly and steps back when closed
- Everything saves as BetterShot: Exported screenshots and recordings are named
BetterShot_plus the timestamp, recording projects use the.bettershotrecextension, and the support folders, sidecar files, and alerts all carry the BetterShot name - One save folder for everything: Screenshots and exported recordings land in the same place, your Desktop by default, chosen once under Settings > General > Save to. The format and quality pickers there govern editor exports too, so nothing quietly disappears into a Pictures subfolder any more
- MP4 is the default everywhere: Exports, quick saves, and suggested file names all produce MP4, and the cloud copy of a share is always an MP4 even when compression cannot shrink the file, so a shared link plays in any browser, on Windows, and in Slack. A QuickTime master is rewrapped without re-encoding on the way out, and MOV stays one tap away in Export Options
- Undo and Redo are buttons again: Both editors carry Undo and Redo in the toolbar, stepping through the same history as ⌘Z and ⇧⌘Z and dimming when there is nothing left to step through
- The canvas sits on a plain background: The dotted workspace pattern behind the editors is gone; both now rest on the native window background
- The upload popover asks one thing: a title. The comments-and-likes toggle went away with the worker service behind it; share pages are static files served from your own bucket
- Clip controls moved to the transport bar: Split, Delete and the clip count sit beside the timecode under the player instead of in a strip above the timeline, which gives the timeline back a row
- Crop is a mode, not a permanent panel: Entering crop shows the crop box, and leaving it puts the box away in both the image and the video editor. Crop handles stay flat while cropping even when a 3D pose is applied, so they remain grabbable
- The face cam bubble is draggable in the editor: Position it in the preview and the export follows, instead of being locked to where capture left it
- Rebuilt clip timeline: Cuts occupy real space on the timeline, delete works on the selected clip, and the drag math for trimming and scrubbing was rewritten so handles track the pointer 1:1
- Export and Cloud Share sit side by side: The editor toolbar has two buttons. Export renders and saves locally; Cloud Share uploads and copies a link. With a bucket connected, Cloud Share is a split button whose menu re-copies the last link, opens it, or hands it to Mail, Messages or Slack through the system share sheet. Without one, it opens the cloud settings instead of hiding
- A share shows you the link: Finishing an upload used to flash a "Link Copied" toast for two and a half seconds and take the link away with it. It now opens a card that stays: the URL sits in a selectable field with Copy Link, Open and a dismiss, the link is still on the clipboard the moment the card appears, and the card materializes on one critically damped spring, scale, blur and position together, rather than sliding ten pixels on a timed fade
- The recording bar remembers only positions you chose: Dragging the bar saves where you put it, but programmatic repositioning no longer counts as a drag, so an automatic placement can no longer be saved and then restored forever. A saved position is also only reused when it fits entirely on a screen
- Roomier recording bar: Taller controls, wider spacing between them, more generous padding around the edges, and a softer corner radius. The bar now sits centered above the bottom of the screen with clearance instead of hugging the bottom edge
- Reduce Motion is respected: The recording bar's show and hide animations fall back to an instant transition when
accessibilityDisplayShouldReduceMotionis set - Pause and resume rebuilt: Pausing now compacts presentation timestamps properly instead of leaving a gap, so paused time no longer stretches the exported timeline
- Restart keeps your source: Restarting a recording replays whatever you were capturing, including the region you selected, instead of always restarting full screen
- Capture callbacks off the main actor: The
SCStreamwrapper isNSLock-guarded so ScreenCaptureKit callbacks stay off the main actor under Swift 6 strict concurrency - New inspector sliders in both editors: Padding, Corner Radius, Shadow, Redaction Density, and Zoom Amount are now label-in-track scrubbers instead of stock macOS sliders. The whole row is draggable, so there is no thumb to chase; hovering reveals calibrated tick marks and the current position; and the value beside it is an editable field, so you can type an exact number, or nudge with the arrow keys. Shared by the image editor and the video editor so both read as one control system
Fixed
- Image share upload: Sharing a screenshot that had no annotations silently did nothing, because the editor only uploaded after a successful re-render of edits. An untouched screenshot now uploads the original file, so every image gets compressed, published to your R2 bucket, and hands you a link, exactly like video sharing
- Default Look actually applies: The background, padding, and corner radius chosen in Settings > Default Look now seed both the image editor and the Recording Studio. A saved editor document or an explicit style preset still wins, and an untouched Default Look leaves the editors on their own defaults
- Recordings missing from Settings: The two history stores were writing incompatible data to the same
history.json, so each save wiped the other's records. The editor history moved to its owneditorHistory.jsonwith a one-time migration, and Settings > Recordings now reads the recording packages on disk directly, so every recording, including ones made before this release, shows up in the list - Delete stays in sync: Deleting a recording from Settings removes the whole recording package and its editor project, and deleting from the editor history clears the matching Settings entry, in both directions without leftovers
- Version shown in the app matches the release: The build was stamping a version hardcoded in the project file instead of the one in
version.json, so the menu bar kept saying the wrong number. Every build now syncs its version and build number fromversion.json, andmake runrestarts the app instead of foregrounding the stale copy - Dragging the preview hands over the real file: Dragging a capture out of the floating preview used to give receivers a temporary copy under
~/Library/Caches, so drops referenced a cache file that could vanish. The drag now delivers the original file itself - Export can no longer fire on a failed load: If the editor opens on a file that has gone missing, Export and Share now disable instead of running and failing with a confusing error
- Annotations have a visible Delete button: The image editor toolbar gained a Delete button that removes the selected annotation, enabled only when something is selected. The Delete key still works as before
- Recording no longer crashes the moment it starts: With the face cam on, the very first camera frame killed the app. The sidecar writer opened its file before attaching the video input, and
AVAssetWriterthrows an uncaught exception for any input added afterstartWriting, so recording died on frame one with an abort rather than an error. The input is attached first and the file opened second, and appending a frame now happens under the same lock that retires the writer, so stopping a recording can no longer land a frame on an input that has already been finished - The face cam mouth matches the microphone: The cam file was written by one object and timed by another.
AVCaptureMovieFileOutputdecided the sidecar's own zero from the first frame it managed to write, while a second video output reported the first frame it saw, which arrives several frames earlier while the movie writer is still starting up. The editor lined the cam up against that earlier instant, so the face ran ahead of the voice by a variable tenth to a third of a second. The cam is now written by an asset writer fed from the same video output that reports the clock, so the frame that starts the file is the frame the offset is measured from, and the editor's preview resyncs at 80 ms of drift instead of 150 ms - The pointer size slider does something: The drawn cursor was rendered at about three quarters the size of a real macOS pointer and the slider stopped at 2x, so moving it looked like nothing happened. The pointer now starts life-size and the slider runs to 3x
- Click highlights are visible: The ring was drawn in plain white for 0.42s, which is invisible on a light window. It now carries a dark halo under the white stroke and lingers 0.55s, in the preview and the export alike
- Color presets apply while paused: Picking a grade with the player stopped repainted nothing, because the preview kept the first filter chain it built and the follow-up seek landed on the frame it was already showing. A paused player now gets a fresh chain per change, so every preset shows on the canvas rather than only in its swatch
- Zoom no longer needs a switch: The zoom inspector acts on cues directly rather than gating them behind a toggle
- Exports stop failing silently: The frame pump treated a reader that gave up mid-render exactly like a reader that finished, so a failed read marked the inputs done and the writer closed a truncated or empty file. It now surfaces the reader's own error, and an export that produced no frames fails instead of writing a stub
- Export failures say what went wrong: "The operation could not be completed" is AVFoundation's placeholder, not a diagnosis. The toast now carries the failure reason, the underlying error, and the domain and code behind it
- Saving to a protected folder reports itself: The render lands in the temporary directory and only the finished file moves to your save folder, so a Desktop or Documents permission block reads as "could not be saved to /Users/you/Desktop" instead of an opaque AVFoundation code
- Every recording crashed the instant it started: A closure written inside a
@MainActorfunction inherits main-actor isolation unless its parameter type is@Sendable. The pointer sampler's timer handler, the three ScreenCaptureKit sample callbacks, and the updater's download delegate were all assigned that way and then invoked on background queues, so Swift 6's isolation check raisedEXC_BREAKPOINTon the first pointer sample of every recording, in every mode. All of them are now explicitly@Sendable, and the capture callbacks moved behind the stream's existing lock, which also removes an unsynchronized read and write of those properties - Every capture was stored up to three times: A screenshot kept its original in Application Support, its beautified export in the save folder, and a byte-identical second copy of the original under
BetterShot/bases/. The editor now maps an export back to the original through history instead of that duplicate, recordings are referenced where they are written rather than copied into Application Support (which cost a full second copy of every video), and re-exporting from either editor updates the existing history entry instead of importing another copy (#99) - History grew without limit: History now keeps the 100 most recent captures by default, configurable in Settings > General > History (50 to 500, or unlimited). Trimming removes only the originals BetterShot keeps in Application Support, never files in your save folder. Leftover copies in
BetterShot/bases/from earlier versions are pruned at launch (#99) - Share uploaded the raw recording: The Share button uploaded the original capture, so trim, crop, zoom, and background effects were silently dropped from every shared link. It now renders your edits into a temporary file, uploads that, and cleans up afterwards. Unedited recordings still upload the original file with no re-encode
- Recording bar sat in the bottom-left corner: The bar measured itself by casting its content view to
NSHostingView<RecordingBarRootView>, but the view is built with an.environmentmodifier applied, so the real type isNSHostingView<ModifiedContent<...>>and the cast always failed. The measurement guard returned early and the bar's frame was never set at all, leaving it at its construction origin in the bottom-left corner. It now measures throughNSView.fittingSize, which needs no cast, and centers above the bottom edge - Microphone and camera entitlements were wiped on every project regeneration:
project.ymlownsBetterShot.entitlements, so XcodeGen rewrote the hand-added microphone entitlement back to an empty dictionary each time the project was regenerated. Both device entitlements are now declared inproject.ymland survive regeneration - Valid R2 credentials reported as invalid: Test Connection signed a request against the bucket root, which needs list permission that an Object Read & Write token does not have. It now probes an object key inside the bucket instead
- Zoom followed the cursor to the wrong place on region recordings: Pointer samples are captured in Quartz global coordinates, but the region rect was being converted to AppKit coordinates before being handed to the sampler, so zoom tracked an inverted Y position on any area recording
- History and Videos tabs stuttered while scrolling:
HistoryStoreis@MainActor, so thumbnail decoding hopped back onto the main actor and ranCGImageSourceandAVAssetImageGeneratorwork there. Decoding is now genuinely off the main actor - Full-size decode after every screenshot: The floating preview card decoded the entire screenshot on the main actor for a 130x98 point card, roughly 60 MB per shot on a 5K display. It now decodes a 260 px thumbnail off the main actor
- Updater leaked a URLSession: A delegate-based
URLSessionwas never invalidated, so it and its delegate outlived every update download (#90, thanks @zergzorg) - Video editor leaked AVPlayer time observers: The previous observer was not released when loading a new video (#91, thanks @zergzorg)
Known limitations
- Auto-zoom overrides manual crop at export: when zoom is enabled, the crop rectangle is ignored
- The face cam bubble is captured from the screen, so it appears in full screen and area recordings but not in single-window recordings, which capture only that window's own content
- Zoom cues are derived from clicks, so a recording with no clicks produces no cues
Download
| File | Architecture |
|---|---|
BetterShot-0.4.0_arm64.dmg
| Apple Silicon (M1/M2/M3/M4) |
BetterShot-0.4.0_x86_64.dmg
| Intel |