Skip to content

Video Recorder

Record your camera and/or screen with mic, entirely on-device

Overview

Video Recorder captures your camera, your screen, and your microphone — alone or together — as a single video, all in the browser. It works like a tiny studio: the preview is always live, the controls sit right over it, and you can flip any source on or off at any time, even while you are recording. It is part of the kimtos new-tab app suite.

Everything is fully on-device. There is no backend, no account, and no upload: capture, compositing, encoding, and storage all happen in your browser, and every recording lives in IndexedDB.

Highlights

  • Three sources — camera, screen (via screen/tab sharing), and microphone. Use any combination.
  • Live studio preview — the compositing canvas is the preview, so what you see is exactly what gets recorded.
  • Toggle anything mid-recording — turn the camera, screen, or mic on or off without stopping or breaking the clip.
  • Camera bubble — when you record screen + camera, the camera is overlaid as a small bubble in a corner you choose.
  • Mirror — the camera is shown as a natural mirrored self-view by default; turn mirror off so on-camera text and whiteboards read the right way round.
  • System audio — optionally mix in the shared screen's or tab's sound.
  • Quality presets — 720p (smoother) or 1080p (sharper), at 30 fps.
  • Reduce noise — an optional, on-device pass that cleans background noise from the audio after recording. Chosen before you record.
  • Countdown — an optional 3-2-1 (or 5-second) overlay before recording starts.
  • Pause and resume — paused time is excluded from both the timer and the saved clip, which stays gapless.
  • History sidebar — every recording is saved locally and played back with a custom video player. Download as .webm, or multi-select and batch-delete.

Privacy model

Video Recorder never sends anything anywhere. Every stage runs on your machine:

  • Capture uses the browser's camera, screen, and microphone APIs directly.
  • Compositing and encoding happen in the page — frames are drawn to a canvas and encoded with the browser's own codecs (WebCodecs, or MediaRecorder as a fallback). The WebM muxer is pure JavaScript that runs locally.
  • Reduce noise runs an on-device model in the browser (it downloads once, about 16 MB, then works offline).
  • Storage writes each recording as a binary blob in IndexedDB.

Nothing is uploaded, and there are no analytics or third-party calls.

Getting Started

Open Video Recorder from the dock. It opens straight into the studio — a live preview with a row of source chips and a big Record button beneath it. Until a camera or screen is on, the preview shows a placeholder.

Choosing your sources

Each source is a chip you tap to toggle. Your choices are remembered for next time.

  • Camera — turns on your webcam. The first time, the browser asks for camera permission. The camera fills the frame when it is the only video source.
  • Screen — starts screen or tab sharing. The browser asks which screen, window, or tab to share. When both screen and camera are on, the camera shrinks into a bubble over the screen.
  • Mic — adds your microphone. Toggling the mic is an instant mute/unmute, so it never interrupts an in-progress recording.

If you stop a share from the browser's own sharing bar, the screen simply drops and the recording keeps going with whatever else is on.

Toggling sources mid-recording

You can flip any chip on or off while recording — add your screen partway through, drop the camera, mute the mic — and the clip never breaks. (The one exception is Reduce noise, which is locked once recording starts.)

Mirror and system audio

Two extra chips fine-tune the mix; they apply live, with no interruption:

  • Mirror — on by default, showing your camera as a natural self-view. Turn it off so text or a whiteboard in front of the camera reads correctly. This chip is only active when the camera is on.
  • System audio — includes the sound of the screen or tab you are sharing. Active only when the screen is on; toggling it connects or disconnects that audio instantly without touching the share itself.

Quality, noise, countdown, and bubble position

These live in Settings → Apps → Video Recorder:

SettingWhat it does
Recording quality720p (smoother, lighter) or 1080p (sharper, better for screen text). Frame rate is 30 fps.
Reduce noiseCleans background noise from the audio after recording, using the on-device denoise model (~16 MB on first use). It re-encodes the clip, taking roughly as long as the recording. Choose it before recording.
Camera bubble positionWhich corner the camera bubble sits in for screen + camera recordings.
Countdown before recordingOff, 3 seconds, or 5 seconds — a 3-2-1 overlay after you press Record.

Reduce noise is also a chip in the studio, so you can flip it before you start.

Recording

  1. Turn on at least one video source (camera or screen).
  2. Press Record. If a countdown is set, a 3-2-1 overlay plays first; pressing Record again during the countdown cancels it.
  3. While recording you get a live timer, a REC badge, and a Pause button. Pausing excludes that time from the timer and the saved clip.
  4. Press Stop to finish. If Reduce noise is on, a short "Cleaning audio…" progress step runs, then the recording is saved.

History sidebar and the player

The left sidebar lists your recordings, newest first (up to 20 are kept). Click one to open it in the custom video player — your own controls, not the browser's native ones. From the result screen you can:

  • Download the clip as a .webm file.
  • Record another to jump back to the studio.

Each row also has a menu with Open and Download.

Multi-select delete

To remove several recordings at once, click Select in the sidebar (or drag across the rows), tick the ones you want, and choose Delete (N). A toast lets you undo for a few seconds. Deleting a recording also removes its stored video data.

Architecture

Video Recorder is built around one idea: record a stable pair of tracks while the real inputs change underneath them. That is what lets you toggle camera, screen, and mic at any time — even mid-recording — without the clip ever breaking. The code lives in extension/src/apps/vidrec/.

The persistent mixer trick

A MediaRecorder (or a WebCodecs encoder) does not like tracks appearing and disappearing — stop a camera mid-recording and the recording falls apart. So the app never records the camera or screen directly. Instead, when the studio opens it creates a persistent mixer that exposes exactly two outputs for the whole session:

  • One canvas video track. A fixed-size <canvas> (sized to the quality preset) is redrawn ~30 times a second. Each frame it paints the screen (contained), then the camera — as a corner bubble over the screen, or full-frame on its own, and mirrored unless you turned mirror off. This canvas is the live preview shown in the page, so the preview and the recording are always identical.
  • One mixed-audio track. A Web Audio MediaStreamDestination is the single audio output. The mic, and optionally the shared screen's audio, are connected to it as source nodes.

The camera, screen, and mic streams are the swappable inputs. Toggling a source acquires or releases that real stream and points the mixer at it (setScreen, setCamera, setMic, setScreenAudio, setMirror) — but the mixer's two output tracks never change. The recorder is wired to those two outputs once, so it keeps running smoothly while inputs come and go. The source <video> elements are kept attached off-screen so the browser does not suspend their decoding, and the mic is muted by flipping its track's enabled flag rather than re-acquiring it.

When you stop, if Reduce noise is on, the clip is run through an on-device denoise model and then re-muxed: the video is played back while the cleaned audio is fed alongside it and the pair is re-recorded (browsers cannot swap an audio track in place). The result is saved to history.

Modules

FileRole
index.tsThe app descriptor and UI — the studio preview, source chips, record/pause controls, countdown, the persistent createMixer, the post-recording denoise + re-mux, and saving to history.
lib/videoEncoderRecorder.tsHardware-accelerated recording with WebCodecs (VideoEncoder/AudioEncoder) muxed to WebM with webm-muxer. Pulls frames from the mixer canvas and audio from the mixed track; supports gapless pause/resume. Falls back to MediaRecorder where WebCodecs is unavailable.
components/historySidebar.tsThe recordings sidebar — list, open, per-row menu, and multi-select batch delete.
components/videoPlayer.tsThe custom video player used on the result screen (not native controls).
lib/engines/denoise.tsThe optional on-device audio denoise model, lazy-loaded only when Reduce noise runs.
lib/dataStore.tsLocal storage — recordings (metadata in a collection, the WebM as a blob) live in IndexedDB, keyed under history:vidrec.

The encoder and muxer are imported lazily, only when you actually start recording, so they stay out of the new-tab bundle.

Capture flow

Camera, screen, and mic feed into the mixer's two fixed tracks; the recorder encodes those tracks to a WebM blob, which is saved to the local history. Toggling a source swaps an input on the left without disturbing the path on the right.

Encoding details

The WebCodecs path (createRecorder) keeps encoding off the main thread for smooth real-time capture:

  • It picks a codec the browser can actually encode at the chosen size — VP9 (with a level chosen to fit the resolution and frame rate) or VP8 — and Opus for audio.
  • addFrame() is called once per composited canvas draw; frames are dropped if the encoder falls behind, so a backlog never compounds.
  • Pause removes paused wall-clock time from both video and audio timestamps, so the saved clip stays gapless and in sync.

Where WebCodecs is unavailable, the app falls back to MediaRecorder over a captureStream of the same canvas plus the mixed audio. Both paths expose the same stop() -> Promise<Blob>, pause(), and resume(), so the rest of the app does not care which one is running. The default codec preference is VP8 (its software encoder is far lighter than VP9 for real-time screen + camera capture).

Persistence

Each recording is one IndexedDB record plus one blob:

{ id, name, at, videoKey, dur, mime }   // video bytes stored at videoKey

The history keeps the most recent 20 recordings; older ones are pruned (record and blob together). Deleting a recording removes both its metadata and its video blob, and uninstalling the app offers to clear all history:vidrec data at once.