Skip to main content

CainAble

Two-machine presenting from one Chrome session.

  • Cain (presenter laptop): Chrome extension. Mirrors the current tab, captures call audio (Meet / Zoom web) and your mic, and sends the focused text box on a hotkey.
  • Able (laptop on the big monitor): a web page served by the backend. Shows the mirrored tab, live captions, and Claude's streamed answers. The audience can ask questions here too. Answers appear only on Able.
Cain extension ──WebRTC video (via TURN)──▶ Able page
      │  room WS (signaling, text box)          ▲  room WS (transcripts, answers)
      │  audio WS (16 kHz PCM, 2ch)              │
      └────────────▶  FastAPI backend  ──────────┘
                        ├─ Deepgram Nova-3 streaming STT (ch0 = call, ch1 = presenter mic)
                        └─ Claude Opus 5.5 (streaming, effort=low, screen frame attached)

Install

pip install cainable        # or: uv tool install cainable / pipx install cainable

Requires Python 3.11+ and Chrome 116+.

Setup

1. Backend

mkdir cainable-run && cd cainable-run
cainable init               # writes .env; fill in ANTHROPIC_API_KEY, DEEPGRAM_API_KEY, TURN creds, ACCESS_KEY
cainable serve              # http://0.0.0.0:7444  (--port, --host, --env-file to change)

Both machines must reach it over HTTPS. Either deploy it (Fly.io, Render, a VM), or tunnel it: cloudflared tunnel --url http://localhost:7444 gives you a public https://….trycloudflare.com URL.

TURN is needed over the internet. Without it, most home and corporate NATs block the video. The simplest option is Cloudflare Realtime TURN (generous free tier): create a TURN key in the Cloudflare dashboard and put the key ID and API token in .env. The backend hands out short-lived credentials.

2. Able (display machine)

Open https://<backend>/?key=<ACCESS_KEY> and press F for full screen. Set ACCESS_KEY in .env; if you leave it empty, the backend generates one and prints it at startup. It shows a 6-digit room code. The code stays in the URL hash, so a reload rejoins the same room.

3. Cain (presenter machine)

  1. Run cainable extension (copies the extension to ./cainable-extension), then chrome://extensions → Developer mode → Load unpacked → select that folder.
  2. Open the popup, enter the backend URL, room code and access key, then Connect.
  3. Go to the tab you want to show and press Alt+Shift+M (or Mirror this tab). Press it again on a different tab to switch. The video track is swapped with no reconnect.
  4. Optional:
    • Alt+Shift+A on the Meet / Zoom web tab marks it as call audio. Without it, the mirrored tab's audio is transcribed.
    • My mic → Turn on: the first time, it opens a tab to grant mic permission.
    • Alt+Shift+S sends the focused text box (or the selected text) to Able, which answers it.

Rebind shortcuts at chrome://extensions/shortcuts.

How answers are triggered (all from Able)

Trigger Default
Text box sent from Cain (Alt+Shift+S) always
Question (…?) heard on the call on
Question heard on the presenter mic off
Clicking a caption line always
Typing in Able's ask box always

Each question carries the current mirrored frame (toggle in ⚙), the rolling transcript, the latest shared text, and the last 6 Q&A turns.

Latency and quality knobs

  • CLAUDE_EFFORT=low: fastest first token on Opus 5.5. Raise it for harder questions.
  • CLAUDE_FAST_MODE=1: Opus fast mode, roughly 2.5x faster output at 2x the price (Claude API only).
  • Server-side refusal fallbacks (fallbacks="default") are enabled, so a declined request is retried on a fallback model.
  • Video: VP9 preferred, 6 Mbps cap, contentHint=detail, maintain-resolution. Slide text stays sharp at the cost of frame rate on bad links.

Development

git clone <repo> && cd CainAble
uv sync                      # installs the package in editable mode plus dev tools
uv run pytest
uv run cainable serve --reload

Layout: src/cainable/ holds the backend (app.py, stt.py, llm.py, ice.py, rooms.py), the Able page (able/) and the Chrome extension (extension/).

Limitations

  • Call audio works only for browser calls. Zoom / Teams desktop apps need a virtual audio device (e.g. BlackHole on macOS) selected as the mic.
  • The text grab can't read canvas-rendered editors (Google Docs, Figma). Select the text instead, or use Able's ask box.
  • Switching the mirrored tab needs the hotkey or popup. Chrome requires a user gesture per tab capture.
  • Rooms live in memory. Restarting the backend creates new codes (Able picks a new one automatically).

Metadata

Release files for cainable 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for cainable 0.1.0
File Size Uploaded Provenance
cainable-0.1.0.tar.gz 29.0 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for cainable 0.1.0
File Interpreter ABI Platform
cainable-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 64.8 kB

Release files / cainable-0.1.0.tar.gz

Download URL cainable-0.1.0.tar.gz
Size 29.0 kB
Tags Source
SHA-256 checksum
How to use checksums
f66846ad87e5a16780384438378d2ed233bca1412fb725771cf024e15d127e2e
BLAKE2b-256 checksum
How to use checksums
25a69a41bcfd84a09a82198fb88a415801e0aa536844541dfe3eaba752cd8ef0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.

Transparency log

Release files / cainable-0.1.0-py3-none-any.whl

Download URL cainable-0.1.0-py3-none-any.whl
Size 35.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3c127c8f45a692d34999a3f15dfd5765dde4d95ea9a31963daac4ac711e785d1
BLAKE2b-256 checksum
How to use checksums
adc805c777a310d437a0d078475f76c0e3060dfff8848f5c43286241ec0181e8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.

Transparency log

Security for cainable 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for cainable-0.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page