Build Screen Studio

YESback to the verdict

0%0 of 20 items done

Saved on this device only. Tick prerequisites first, then work the phases in order · do not start one until the checks above it pass.

A recorder that captures the screen flat while writing a separate high-frequency cursor and click track, then generates smooth zoom keyframes from that track in post, applies padding, rounded corners and a background, and renders a finished video you never had to edit by hand.

estimated effort weekendthe files for this build are in the project pack

LanguageSwift 6 and SwiftUI in XcodeCaptureScreenCaptureKit for video, plus a separate cursor event trackCompositionCore Image and Metal, rendered through AVAssetWriterInterpolationCubic easing over generated keyframesStorageThe flat recording plus a sidecar JSON project file

Before step 1

Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.

  1. installfree if you already own a Mac

    Why ScreenCaptureKit needs 12.3 as a floor, and the composite pipeline is GPU work. An old integrated GPU will export slower than real time.

    Get it Apple menu, then About This Mac.

    Verify About This Mac reports macOS 13 or higher

  2. installfree

    Why Builds the app and provides Instruments, which you will need when the export is slow.

    Get it Mac App Store or developer.apple.com/xcode. open ↗

    Verify xcodebuild -version prints 16 or higher

  3. decidefree

    Why Screen Recording is required to capture at all. Microphone is optional and only for narration.

    Get it System Settings, then Privacy and Security. Both must be re-granted during development whenever the binary changes signature or path.

    Verify A test capture returns pixels rather than a black frame

  4. have readyfree

    Why The thresholds in Phase 2 cannot be chosen in the abstract. You need a form being filled, a terminal session and a slow read to see where the generator over-triggers.

    Get it Record them yourself before writing the detection code, and keep them as fixtures.

    Verify Three recordings with their cursor tracks are saved as test fixtures

  5. 99 USD per year

    Why Product phase only. Without it there is no notarization and a Gatekeeper warning on every other Mac.

    Get it Enrol at developer.apple.com/programs. open ↗

    Verify A Developer ID Application certificate appears in Xcode settings

Data model

Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.

- recordings(id, video_path, width, height, scale, fps, started_at, duration_ms)
- cursor_samples(recording_id, t_ms, x, y) · sampled at a high fixed rate, independent of video frame rate
- cursor_events(recording_id, t_ms, kind) · kind is click, drag_start, drag_end, keypress or scroll
- keyframes(id, recording_id, t_ms, scale, center_x, center_y, easing) · generated in Phase 2, editable afterwards
- projects(id, recording_id, padding, corner_radius, background_json, trim_start_ms, trim_end_ms)
- The flat recording is never modified. Every effect is a function of the source plus the project row, which is what makes re-rendering at a different size free.

Environment variables

These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.

VariableNeededExampleWhere the value comes from
RECORDINGS_DIRrequired~/Movies/ScreenStudioWhere flat recordings and project files are written. These are large, so pick a disk with room.
CURSOR_SAMPLE_HZrequired120Cursor sampling rate. Must be well above the video frame rate or the motion looks stepped.
ZOOM_MIN_INTERVAL_MSrequired2500Minimum time between zoom-ins. Raising this calms a busy video.
ZOOM_MIN_DWELL_MSrequired600How long the cursor must stay in an area to count as intent. Lowering it makes the video restless.
ZOOM_MAX_SCALErequired2.2How far in the automatic zoom will go. Beyond about two and a half the source pixels start to show.
SPARKLE_FEED_URLoptionalhttps://example.com/appcast.xmlProduct phase only.

The build, in order

  1. Record flat, and capture the cursor track

    A plain recording exists alongside a dense, accurate record of where the cursor was and what it did.

    1. SCStream configured for the display's real pixel dimensions. Write with AVAssetWriter, starting the session from the first video sample's timestamp.

      Files Recorder.swift

    2. A high-rate timer reading the global cursor position, timestamped against the same clock as the video session. Log clicks with an event tap or a global monitor.

      Files CursorTrack.swift

    3. Record a deliberate click at a known screen position, then confirm the logged event timestamp matches the frame where the click is visible.

      Files test/alignment.md

    done when · tick each as it passes
    watch out
    • Do not apply zoom while recording. A zoomed recording throws away the pixels outside the zoom, so a badly chosen zoom is permanent and a re-render is impossible. Record everything, decide later.
    • Sample the cursor far more often than the video frame rate. Sampling per frame gives you a stair-stepped path that produces visibly jerky motion once you smooth and amplify it.
  2. Generating zoom from the track

    The zoom keyframes are computed from what the cursor did, and they are good enough that you rarely touch them.

    1. Cluster click events in time and space. Treat sustained dwell within a small radius as intent too, because reading and typing matter as much as clicking.

      Files Intent.swift

    2. Zoom in slightly before the action so the viewer arrives with it, and hold briefly after so the result is visible before pulling out.

      Files Keyframes.swift

    3. A minimum interval between zooms, a minimum dwell to qualify, and a maximum zoom rate. These three numbers are the whole feel of the product.

      Files Keyframes.swift

    done when · tick each as it passes
    watch out
    • Following the cursor continuously produces motion sickness. The cursor moves constantly and most of that movement means nothing. Zoom on intent, which is clicks and sustained dwell, not on position.
  3. Easing and framing

    The motion looks designed rather than computed, and the frame never shows the edge of the source.

    1. Ease in and out between keyframe pairs. Compare against linear side by side once; you will not go back.

      Files Easing.swift

    2. Compute the visible rectangle at the current scale and clamp its centre so the rectangle stays inside the source. Test the corners explicitly.

      Files Framing.swift

    3. A Core Image pipeline: scale and translate the source, round the corners, and draw over a background colour or gradient.

      Files Composite.swift

    done when · tick each as it passes
    watch out
    • Clamp the centre point, not the offset. Clamping after computing the offset lets the frame drift past the edge of the source at high zoom levels and shows background where video should be, usually at exactly the moment of most interest.
  4. Rendering and export

    The finished video comes out at the size you asked for, and rendering is not slower than watching.

    1. One Metal-backed CIContext reused across frames. Creating a context per frame is the single most common reason exports crawl.

      Files Renderer.swift

    2. Named presets with fixed pixel sizes. Assert the output dimensions rather than trusting the scale arithmetic.

      Files Presets.swift

    3. Pass the original audio track through rather than re-encoding it, and confirm sync at the end of a long export.

      Files Renderer.swift

    done when · tick each as it passes
    watch out
    • Compositing on the CPU makes a two minute recording take longer than two minutes to export, which turns a pleasant tool into an errand. Keep the pipeline on the GPU through Core Image with a Metal-backed context, and measure the export against real-time before adding any more effects.
  5. Trim and manual correction

    The one zoom the generator got wrong takes ten seconds to fix.

    1. Video thumbnails, click markers and keyframe handles on one scrubbable track.

      Files Timeline.swift

    2. Drag to move, delete, insert. Preview updates from the source, which is why nothing was baked in.

      Files Timeline.swift

    3. Mark keyframes as generated or manual, and warn before regeneration discards manual ones.

      Files Keyframes.swift

    done when · tick each as it passes
    watch out
    • Automatic generation will be wrong occasionally, and a tool with no manual override forces a re-record for a five second mistake. The keyframes are data, so make them editable from the beginning rather than treating generation as final.
what this build does not replace
after v1, if you want it

Need the files? The project pack on the verdict page hands your agent the whole brief · more screen recording.