Skip to content

Glossary

This product has its own vocabulary and the code uses it everywhere, in class names, table names and filenames. Reading the codebase without it is harder than it needs to be, and two of these terms are easy to confuse in a way that matters.

The product

Circle — a closed group of friends. Everything belongs to one: the roulette, the turns, the vlogs. You join by invite code and there is no discovery, no search and no public anything. circles table, Circle model.

Cadence — how often the roulette spins in a circle: every 3, 5 or 7 days. Chosen when the circle is created and stored on it, not on the turn.

Draw — the roulette picking whose turn it is. Weighted, not uniform: it favours whoever has had the fewest turns, and it will not pick the previous vlogger again unless there is nobody else. app/services/draw.py.

Turn — one person's window to record and publish. It has a deadline, a status, and at most one vlog. The whole product is a state machine over this one object; see Turn states below.

Vlogger — whoever the draw landed on. Only they can record or upload for that turn; the API checks it on every route that touches footage.

Premiere — the 24 hours after a vlog is published, during which the circle can watch it. When it ends the turn is archived and the roulette spins again.

Strike — what a vlogger gets for letting a deadline pass with nothing recorded. Three of them and they are removed from the circle. A re-roll is deliberately not a strike: that is the group choosing, not a failure.

Re-roll — the circle voting to hand the turn to somebody else. A strict majority is enough while the turn is open. During a premiere the same vote ends it early, and that one requires everybody, because it takes the vlog away from anyone who has not watched yet.

The footage

These three are not synonyms, and the difference is why some code looks the way it does.

Take — one continuous act of recording: from pressing record to pressing stop. This is the unit a person thinks in, and the unit the review screen shows.

Segment — one file on disk. A take is usually one segment, but flipping the camera mid-take closes one file and opens another, so a single take can be several segments. The person did not stop recording, so the edit must not treat it as two takes. groupIntoTakes in src/storage/clipList.ts is what puts them back together.

Clip — the generic entry in the manifest: a segment of camera footage, or a video or photo brought in from the gallery. Photos are held as stills for a few seconds and given silence for audio.

Manifest — the JSON file on the phone that lists a turn's clips, their order, durations and render state. It is the source of truth while recording, survives the app being killed, and is what lets somebody record on Monday and finish on Wednesday. src/storage/clipVault.ts.

Allowance — how much raw footage the camera will accept, which is more than the finished vlog can hold (1.5x by default). Recording more than the result can fit is expected; the edit trims the last take to fit and says what did not make it.

The edit

EDL — Edit Decision List. A plain description of the finished vlog: which clip, from which second to which second, with what transition. It is computed from the manifest and it is deliberately duplicated in TypeScript (src/edit/edl.ts) and Python (app/services/edl.py), with mirrored tests, so the phone and the server produce the same vlog from the same footage.

Engine — whatever actually turns an EDL into an mp4. src/edit/engine.ts picks between three: the native module on the device, an in-process FFmpeg that no build currently links, and the server fallback. When none of them can run it says none rather than failing somewhere less obvious.

Native enginemobile/modules/vlog-editor, a local Expo module. On Android it decodes, composites through OpenGL and re-encodes with MediaCodec. This is the architecture the product is built on: the phone edits, the server stays small.

Server render — the fallback, in app/services/render.py, which does the same job with FFmpeg. It exists for devices that cannot run the native engine — Expo Go, a build older than the module — and for material the engine says it cannot carry. It is off by default because it turns a light server into a video server.

Loudness normalisation — making takes recorded at different distances from the microphone sit at the same level, so the finished vlog does not jump in volume at every cut.

VAD — voice activity detection, used to find silences. It is implemented and switched off: cutting a person's pauses reads as the app editing over them rather than for them. TRIM_SILENCES in src/edit/trimming.ts is the single constant that decides it.

Turn states

Set in app/models/enums.py. Only PUBLISHED is visible to the rest of the circle; the rest are the vlogger's own progress.

State Means
pending Drawn, nothing recorded yet
recording At least one clip exists on the device
rendering The vlogger closed the window and the phone is exporting
published Uploaded and premiering to the circle
missed The deadline passed with no footage: strike, and the turn moves on
skipped The circle voted to re-roll: no strike, the turn moves on
archived The premiere ended; the vlog is history

Infrastructure

Presigned URL — a signed, expiring permit the server hands the phone so it can upload straight to R2 without the video ever passing through the API. The reason the API machine can stay small.

Development build vs preview build — the development build needs the Metro bundler running on a computer and talks to a local API; the preview build is standalone and points at production. They share a package name, so installing one replaces the other.

Source maps — the translation table that turns a crash in compiled Hermes bytecode back into a filename and a line number. Uploaded to Sentry during the EAS build; without them a mobile stack trace is unreadable.