Skip to content

Closeup

Film any part of a web page in close-up, sharp at any size, with a drawn cursor and sound.

v0.2.0MITGitHubnpm
npm install @cesarvenzac/closeup

Film it opens Closeup on the set. Anywhere on this site, ⌥⇧C opens it.

Use

typescript
import { install } from "@cesarvenzac/closeup";

install(); // ⌥⇧C opens and closes it

Or load it only when it's wanted, on a shortcut of your own:

typescript
addEventListener("keydown", (event) => {
  if (event.code === "KeyC" && event.altKey && event.shiftKey) import("@cesarvenzac/closeup").then((closeup) => closeup.toggle());
});

Or open it on an element, ready to film, from a button of your own, as Film it does:

typescript
import { open } from "@cesarvenzac/closeup";

filmIt.addEventListener("click", () => open({ pick: document.querySelector("#demo") }));

Point at an element (↑ and ↓ widen and narrow the pick) or draw a region, choose a photo or a video, and press Record; Chrome asks once to share the tab. Options set the shape, the room round the pick, a background with its inset, corners and shadow, the cursor and its click ring, the sounds and the quality, and they're kept in the browser.

Scripts

The pen opens an editor on the stage, at the size the take is filmed: click where the cursor goes, and drag to pull a curve's handles, as Figma's pen does. Each point moves there, clicks or types, at a hand's pace, and is tied to the element it's on. While a script plays, the page hears every move and press as a mouse would tell it, and its :hover and :active rules follow the drawn cursor. A script is plain JSON, to keep with the page:

json
{
  "version": 1,
  "start": { "x": 480, "y": 130 },
  "steps": [
    {
      "to": { "x": 450, "y": 110, "on": { "selector": "button.like", "u": 0.5, "v": 0.5 } },
      "action": "click",
      "hover": 0.4,
      "wait": 1
    },
    { "to": { "x": 600, "y": 300 }, "action": "type", "text": "Hello", "wait": 0.6 }
  ]
}

Your own actions

addAction puts a button of your own on the finished take, beside Download, to keep it somewhere:

typescript
import { addAction } from "@cesarvenzac/closeup";

addAction({
  label: "Save to library",
  icon: `<path d="M12 13v8"/><path d="m8 17 4-4 4 4"/>`, // optional: paths on a 24-unit grid
  when: (take) => take.kind === "video", // optional: which takes to offer it on
  async run(take, { progress }) {
    await upload(take.blob, progress); // call progress(0…1) as it goes
    return "Saved."; // said when it's done; a thrown error's message is said instead
  },
});

The button counts up while it works and stays pressed once it's done. addAction returns what takes it away again.

Needs

A Chromium browser on a desktop (Chrome, Edge, Arc, Brave), since Region Capture is theirs. Chrome records H.264 MP4; other builds may record WebM. Another site's stylesheets can't be read, so their hover rules can't follow a script, and the page doesn't scroll during a take. How it stays sharp is in the Lab.