Skip to content

rlorenzo/zoom-icebreaker

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

35 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Icebreaker Tracker

A live roster of who has introduced themselves — for meetings where everyone goes around the room.

In meetings where everyone introduces themselves, somebody always asks "wait, has Alex gone yet?" and the room loses thirty seconds. Icebreaker Tracker puts the answer on screen for everyone: a live roster, who has been marked introduced, and who is coming up next.

Screen-share the page so the whole room can self-pace. It reads Zoom's Participants panel automatically on macOS and Windows, or you type names in by hand — the page looks and works the same either way.

One local process, one command. No Node, no Zoom account, no credentials, and nothing leaves your machine.

No meeting handy? The first screen has a Try a demo button that loads this sample roster so you can click around. Leaving demo mode clears the slate.

Two ways to run it

  • In the browser, no installhttps://rlorenzo.github.io/zoom-icebreaker/. The same page, running entirely client-side: add names by hand, mark people introduced, reorder, set a prompt, screen-share the tab. Your session is kept in the browser (localStorage) so a refresh mid-meeting doesn't lose the slate, and nothing is sent anywhere. The only thing it can't do is read Zoom's panel for you (that needs OS accessibility access) — so it's manual entry, and everything else is identical.
  • Locally, with automatic Zoom reading — one Python command, below. Adds auto-filling the roster from Zoom's Participants panel on macOS and Windows.

Quick start (local app)

uv sync          # one-time: install dependencies
uv run tracker.py

Open http://localhost:3000 and screen-share that browser tab in Zoom. That's the whole setup — names start filling in (or add them yourself), and you tap one button per person as they speak.

Don't have uv? It's a single-binary Python installer — see the uv install guide. Or skip it entirely and use the hosted version.

What you get

  • A live roster that updates for everyone watching the share, no refresh.
  • One-tap "introduced" per person. The counts and the highlighted "up next" row update instantly.
  • Automatic Zoom reading on macOS and Windows — it fills the roster from the Participants panel so you rarely type a name.
  • Manual mode anywhere — no permission or wrong OS? Type names in. The page is identical.
  • Private by design — no account, no history, and nothing sent anywhere. In the local app, state lives only in memory and is gone when you stop the process; the hosted version keeps it in your browser (localStorage) so a refresh doesn't lose the meeting, and clearing it is a click away.

Two views, one screen

The host and the room see the same page, and both matter:

  • The host runs the command, opens the page, and screen-shares it. They are the only person who clicks anything — toggling "introduced" as each person speaks, occasionally adding a name, hitting reset at the end.
  • The room watches over screen-share. They can't interact; they read. They want to know who has gone, who hasn't, and whether they're up next — on compressed video, often on a laptop, sometimes on a phone.

So the layout stays legible on a shared screen and reflows cleanly down to phone width. The "introduced" toggle is always manual, by design: it's a judgement call only the host can make as people actually speak.

You can always mix modes — let it auto-read and still add or remove people by hand.

Auto-reading Zoom

Auto-read watches your screen's accessibility tree to list who is in the Participants panel. It never talks to Zoom's API or SDK, so it doesn't touch Zoom's terms — but it can break when Zoom redesigns the panel (see troubleshooting).

macOS

Auto-read needs pyobjc (installed by uv sync) and macOS Accessibility permission. Grant permission to the app you run the command from (Terminal or iTerm) in System Settings → Privacy & Security → Accessibility, then reopen that terminal.

Start your meeting, open the Participants panel, then run uv run tracker.py.

Windows

Auto-read uses UI Automation via the uiautomation package (also installed by uv sync). No extra permission prompt — UIA is part of the standard Windows accessibility stack.

Start your meeting, open the Participants panel, then run uv run tracker.py. The --anchor-regex, --exclude, and --debug flags below work the same as on macOS; --bundle is macOS-only and ignored here.

Running a meeting

  1. uv run tracker.py, open the URL, and share that tab in Zoom.
  2. People appear automatically (or add them manually). "Started" shows when you began the session.
  3. Tap Mark introduced after each person speaks. The counts and the highlighted "up next" row update live for everyone watching.
  4. Reset session clears it for the next meeting.

Command-line options

--port N          port to serve on (default 3000)
--interval N      seconds between Zoom reads (default 5)
--no-ax           manual entry only, never read Zoom
--anchor-regex    text identifying the participants container
--exclude "a,b"   extra whole-word non-name terms to filter out
--min-len N       minimum name length (default 2)
--debug           print anchor / raw-node diagnostics

Troubleshooting

Zoom's accessibility tree is undocumented and changes between versions. If names don't appear, see what Zoom is exposing and tune the matcher:

uv run ax_dump.py --grep '(?i)participant'
uv run tracker.py --anchor-regex 'participants|attendees' --debug

Known limitations:

  • Virtualized participant lists may only expose names currently scrolled into view.
  • Dial-in users sometimes appear as phone numbers rather than names.
  • It reads who is present, not who has spoken. For an automatic "who has actually spoken" signal, a saved Zoom transcript is the better source — ask if you'd like that ingest added.

Development

uv sync --dev                # install dev tools
uv run pre-commit install    # enable the git hook
uv run pre-commit run --all  # run all checks once

Tooling: ruff (lint + format), bandit (security), lizard (complexity), pymarkdown (markdown), plus biome and html-validate for the web assets. The same checks run on every PR via .github/workflows/ci.yml.

The web assets (index.html, app.js, roster.js, demo.js, engine.js, session.js, styles.css) are read into memory once at startup, so the server never opens a file in response to a request. The trade-off: editing any of them requires restarting the server (Ctrl-C and re-run) to see the change.

The same assets are deployed to GitHub Pages by .github/workflows/pages.yml on every push to main. There, app.js finds no /events backend and falls back to the local in-browser engine (engine.js); tracker.py is not involved at all.

The README clip (docs/demo.webm, VP9) is generated from demo mode, so it never needs a real meeting. With the server running, Playwright installed (npx playwright install chromium), and an ffmpeg with libvpx-vp9 on PATH (brew install ffmpeg, or point $FFMPEG at one), regenerate it with npm run record:demo.

Support

If this saved your meeting a few awkward seconds, you can sponsor the project on GitHub.

About

Live tracker for Zoom icebreaker rounds. Reads the Participants panel via macOS Accessibility to show who's introduced themselves.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors