Getting started
Install Closeup, open it on a shortcut or on an element, and film a first take.
npm install @cesarvenzac/closeupPlain TypeScript with no dependencies. Its interface lives in a shadow root of its own, so the page's styles don't reach it; the only styles it adds to the page hide the real cursor while it films and copy the page's hover rules while a script plays.
Open it
install opens and closes it on ⌥⇧C, left alone while someone types in a field:
import { install } from "@cesarvenzac/closeup";
install(); // ⌥⇧C
install({ shortcut: "Alt+Shift+K" }); // or a shortcut of your ownA shortcut names its key and at least one of ⌥, ⌘ or Ctrl (Alt, Cmd or Meta, Ctrl), with ⇧ (Shift) if you like; it's matched by the key's place, so ⌥ letters still match on a Mac. install returns what takes it away again.
Only when it's wanted
Closeup weighs nothing until it's opened if it's imported on the first press:
addEventListener("keydown", async (event) => {
if (event.code !== "KeyC" || !event.altKey || !event.shiftKey) return;
const closeup = await import("@cesarvenzac/closeup");
closeup.toggle();
});On an element
open({ pick }) opens it on an element, ready to film, from a button of your own:
import { open } from "@cesarvenzac/closeup";
filmIt.addEventListener("click", () => {
open({ pick: document.querySelector("#demo") });
});// Closeup is a package (npm install @cesarvenzac/closeup), loaded only when
// it's wanted. Film it opens it on the set, ready to record.
const film = () =>
import("@cesarvenzac/closeup").then((closeup) => closeup.open({ pick: set.current }));
// It stays sharp by enlarging the page, not the take: <body> is scaled round
// the pick until the frame fills most of the window, so the browser draws it
// large at the screen's own resolution. The layout doesn't change, only how
// big it's drawn.
body.style.transformOrigin = "0 0";
body.style.transform = `translate(${stage.tx}px, ${stage.ty}px) scale(${stage.scale})`;
// Then this tab is captured and cropped to the frame by Region Capture, so
// Closeup's own controls, outside it, never show. Chromium only, for now.
const stream = await navigator.mediaDevices.getDisplayMedia({ video: true, preferCurrentTab: true });
const [track] = stream.getVideoTracks();
await track.cropTo(await CropTarget.fromElement(frame));
// A button of your own on the finished take, as this site's Save to library is.
closeup.addAction({
label: "Save to library",
async run(take, { progress }) {
await upload(take.blob, progress);
return "Saved.";
},
});A first take
- Press ⌥⇧C. Point at what to film (↑ widens the pick to what holds it, ↓ narrows it back), then click. Or switch to a region and draw one.
- Choose a photo or a video. What the take will film is outlined, its shape and the room round it included, and the rest of the page dimmed.
- Press Record (Capture for a photo). Chrome asks to share the tab each time: choose this tab. A video counts down from 3, to get your hand back to the mouse.
- Film: click, type and scroll as you would. Stop, or Esc, ends it.
- The take opens in a sheet, to trim, speed up and download as a video or a GIF.
Needs
A Chromium browser on a desktop, Chrome, Edge, Arc or Brave: Region Capture, which crops the tab to the take, is Chromium's. supportsCapture says whether the browser can:
import { install, supportsCapture } from "@cesarvenzac/closeup";
if (supportsCapture()) install();Chrome records H.264 MP4; other builds may record WebM. Stylesheets from another site can't be read, so their hover rules can't follow a script's cursor, and code that checks event.isTrusted ignores a script's events.