The frame is an array. Change the array.
Start the camera and every frame is pulled out of the stream as a flat
RGBA byte array — four bytes per pixel — passed
to the JavaScript function in the editor, and painted back. The function
starts as the identity transform, returning the frame untouched, so the
first thing you see is your camera exactly as it is. Then you change it.
Subtracting each channel from 255 is a negative. Rotating red into green
into blue is an alien colour cast. Swapping row y with row
height − 1 − y turns the picture
upside down. All twenty-one built-in effects are exactly
that — a few lines of ordinary JavaScript, loaded into the editor
where you can read and edit them, not fixed filters hidden behind a button.
One name, one row, one keystroke
There is no split between “examples” and “real”
effects, because there is no difference in the code: black &
white, flips, invert,
sepia, threshold,
posterize, pixelate, an animated
wave, thermal camera,
X-ray, mirror,
kaleidoscope, light tunnel,
squeeze, stretch, twirl,
a green movie grade and an old TV set sit
in one row under one word. The left and right arrow keys
walk it, so you can point the camera at yourself and hold an arrow down
rather than hunting for a chip.
They are here as code rather than as buttons because that is where they get
interesting: thermal is a lookup into a six-stop colour ramp, squeeze and
stretch are the same three lines of polar arithmetic with one constant
either side of 1, pixelate averages each 24 px block in one pass
and paints it back in a second, movie is three 256-entry lookup tables built
once a frame instead of a curve evaluated 700,000 times, and the old TV set
is a curved tube, alternate scanlines, red-green-blue phosphor stripes and a
hum bar rolling down the picture — constants you can turn up until it
looks like the set you remember.
One number, on a dial
Most of them turn on a single number: the block size, the cutoff, how many
levels, how far the tube bows. An effect that names it
MODIFIER gets a slider on the picture, so it
can be turned while you watch the result — full screen, on a phone,
with the camera pointed at your own face.
The slider holds no value of its own. Every move rewrites that line in
the editor and recompiles, so the code on screen is still the whole
truth about what is running: drag the dial to 8 and
const MODIFIER = 8; is what you find in the source, ready to
copy out. Declare MODIFIER and MODIFIER_RANGE in
an effect you write yourself and it gets a dial too.
Keep what you made
Record encodes the moving picture as a clip — the
canvas, at the processing size, carrying the microphone only if you ticked
Sound, and never the camera track. What you see on the
screen is exactly what is in the file. It opens in
/mp4 BOXES, so you can read the file you just made atom by
atom, and it is never written to disk unless you download it.
Record has a second mode. Timelapse keeps one frame every
interval you choose — from a fifth of a second to thirty seconds
— and replays them at 30 fps, so an afternoon becomes a clip you
can watch: at one frame a second that is 30× faster
than the room, at one frame every ten seconds, 300×. The effect is
applied before anything is recorded, so a pixelated timelapse of a room is
a recording that was never identifiable in the first place.
Every effect is a small piece of arithmetic
Which makes this a good place to learn: pixelate is a mean over a
block, posterize is rounding to a grid, twirl is a
conversion to polar coordinates and back, and each is a three-line change
with your own face attached to the output.
Learn
maths and programming with a photo booth walks through six of them, with
exercises and a 45-minute lesson outline.
It tells you what it is doing
The diagram above the editor is live: it reports the camera’s real
capture resolution, the milliseconds your effect spends on each
frame (with a “?” that says what that number has to
beat), and the frame rate actually reaching the screen — so when a
heavy effect slows down, you can see where the time goes. A
syntax error, a forgotten return or a wrongly sized array is
named in plain language before the video is touched, and a transform that
never returns is terminated by a watchdog instead of freezing the page.
Your camera is never uploaded or transmitted: the pixels go to a Web Worker
on this origin and nowhere else, and a clip you record is held in the
tab’s memory until you download it or leave.
It’s one lens on OmniViewer’s
Webcam Test & Camera Capabilities:
drop a file and every tab is one click away. OmniViewer opens
every file format, entirely in your browser —
no upload, no server.
FAQ
Is my camera feed uploaded anywhere?
No. OmniViewer is a static page with no server-side processing and no analytics of the video: the stream goes from your camera to the page and nowhere else. There is no endpoint to upload to. Clips you record are held in your browser tab and disappear when you leave, unless you download them yourself.
Does the page open my camera without asking?
No. Nothing opens until you have granted this site camera access, and the grant is yours to make in the browser’s own prompt. What happens after that is different: once the permission exists, the Mirror and Transform tabs — the two that are nothing but a picture — open the camera on arrival rather than making you press start again every visit. The page checks whether the permission is already there without prompting for it, using the Permissions API where the browser supports the name and the presence of device labels everywhere else. Press stop and it stays stopped, and revoking the permission in your browser puts the button back.
What resolution does it open at?
The highest one your camera reports it can do. A camera’s real maximum is only readable from a track that is already open, so the first stream on a device is opened with no size constraint and then raised to the maximum getCapabilities() reports — one applyConstraints on the live track, no second permission prompt. The resolution picker on the Preview tab overrides it: pick a specific size, or “Camera’s choice” to send no width or height at all and see what the device volunteers on its own, which is usually a good deal less.
Why does the camera open at a lower resolution than I asked for?
Because a constraint is a request, not a command. An "ideal" constraint is a preference the browser may silently ignore, and a camera already opened by another application will hand you whatever mode it is already running in. The Capabilities tab is built to show exactly this: the resolution you asked for, the range the hardware reports it supports, and the one you actually got, with the disagreement flagged.
Why are my cameras listed without names?
Device labels are hidden until you grant camera access. Before the grant, a page can enumerate your devices and count them but cannot read their labels — that is a deliberate browser privacy mitigation. Press Start camera and allow access, and the names appear immediately. You can watch it happen on the Capabilities tab.
Does this work in every browser?
The preview and the asked/supported/got table work in any modern browser on a secure (https) page. How much the table has to show depends on the browser and the hardware: Chromium reports the widest set of camera controls, and most laptop webcams expose far fewer of them than a phone camera does. The effects need a Web Worker, and recording a clip of them needs both canvas captureStream and MediaRecorder.
What format does the clip recording use?
Whatever the browser supports, preferring MP4 — Safari and Chrome 130+ record MP4, which opens in OmniViewer’s /mp4 toolkit with its full atom tree. Browsers that only record WebM produce a valid file that opens in the universal raw, hex and strings views, since OmniViewer has no dedicated WebM route yet. Clips are capped at 30 seconds so the recording stays in memory safely.
Do the clips I record here contain EXIF or GPS data?
No, and that is half the point of recording them here. A browser capture has no camera make or model, no lens or firmware string, no serial number and no location — the capture path has no access to any of it. One line is added on the way out naming the website that made the file, and the toolkit that reads it will take even that out. Open a clip in /mp4 and compare it against a video from your camera roll to see the difference.
Does the page keep using my camera after I switch tabs?
No. Stopping the camera stops every track, which is what turns the hardware indicator light off; loading a file, pressing close, or leaving the route all do the same. Merely dropping the video element would leave the light on, so the toolkit stops the tracks explicitly.