https://github.com/isene/spot
Presenter spotlight overlay for Linux — pure x86_64 assembly, ~14 KB. Click-through, screen-share-safe, follows the pointer via SHAPE.
https://github.com/isene/spot
Last synced: 10 days ago
JSON representation
Presenter spotlight overlay for Linux — pure x86_64 assembly, ~14 KB. Click-through, screen-share-safe, follows the pointer via SHAPE.
- Host: GitHub
- URL: https://github.com/isene/spot
- Owner: isene
- License: unlicense
- Created: 2026-06-18T16:08:47.000Z (11 days ago)
- Default Branch: master
- Last Pushed: 2026-06-18T19:02:45.000Z (10 days ago)
- Last Synced: 2026-06-18T20:23:41.524Z (10 days ago)
- Language: Assembly
- Size: 70.3 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- awesome-github-repos - isene/spot - Presenter spotlight overlay for Linux — pure x86_64 assembly, ~14 KB. Click-through, screen-share-safe, follows the pointer via SHAPE. (Assembly)
README
# spot - Pure Assembly Presenter Spotlight










Presenter overlays for the [CHasm](https://github.com/isene/chasm)
desktop suite. Three modes from one binary: **spotlight** (dimmed
screen, circular hole follows the cursor), **draw** (click-drag to
annotate), **highlight** (click-drag a rectangle that stays bright
while the surround stays dim). Works on every workspace and over
screen-share (Teams, Discord, Meet capture the composited framebuffer,
which includes us).
Single static ~21 KB ELF, no libc, pure x86_64 NASM. X11 wire protocol
+ SHAPE extension over a Unix socket. Every key passes through to the
focused application — type, click, navigate slides while the overlay
is up. Toggle via the launch key (a second press kills it).
## Modes
| Mode | Status | Bound to |
|------|--------|----------|
| **spotlight** | ✓ shipped (v0.1.0) | `Mod4+Shift+s` (tile) |
| **draw** (annotate) | ✓ shipped (v0.2.0) | `Mod4+Shift+d` (tile) |
| **highlight** (drag-rect) | ✓ shipped (v0.3.0) | `Mod4+Shift+h` (tile) |
## Install
```bash
git clone https://github.com/isene/spot.git
cd spot
make
sudo make install
```
Add toggle bindings to `~/.tilerc` (same key launches and kills):
```
bind Mod4+Shift+s exec sh -c 'pkill -x spot || exec spot'
bind Mod4+Shift+d exec sh -c 'pkill -x spot || exec spot draw'
bind Mod4+Shift+h exec sh -c 'pkill -x spot || exec spot highlight'
```
First press launches spot; second press kills it. Esc and every other
key passes through to whatever application has focus in **spotlight**
mode — type into the terminal, click in the slides, anything underneath
stays interactive while the spotlight is up.
In **draw** mode, click-drag draws strokes onto a frozen snapshot of the
screen. Configure colour and width via env vars:
```
bind Mod4+Shift+d exec sh -c 'pkill -x spot || env SPOT_COLOR=00cc44 SPOT_WIDTH=5 exec spot draw'
```
| Env | Default | Effect |
|-----|---------|--------|
| `SPOT_DIM` | `80` | spotlight surround brightness (0-100) |
| `SPOT_COLOR` | `ff0000` | draw stroke colour (hex `RRGGBB`, optional `#`) |
| `SPOT_WIDTH` | `3` | draw stroke width in pixels |
## Configuration
`SPOT_DIM=<0..100>` env var controls the surround brightness:
| Value | Effect |
|-------|--------|
| `100` | pure black surround |
| `80` | default — `#333333` |
| `50` | medium gray `#808080` |
| `0` | white surround |
```
bind Mod4+Shift+s exec env SPOT_DIM=70 spot
```
v0.1.3+ shows the **actual screen content darkened** (not a solid colour).
At startup, spot does `XGetImage` on root, multiplies every R/G/B channel
by `(100 - SPOT_DIM) / 100`, and uploads the result as the overlay's
back pixmap. No compositor needed — the snapshot is just bytes on the
server side, painted as the window background.
Caveat: it's a **snapshot**. If something behind spot animates / scrolls
/ repaints while spot is running, the dimmed surround stays as it was at
startup. Fine for presenting static slides. Less great for live demos
where windows below are updating; in that case, exit spot and re-launch.
## How it works
- **Snapshot pipeline**, shared by all three modes: `XGetImage` on root
at startup, walk every pixel through a 256-byte LUT that scales each
R/G/B channel by `(100 - SPOT_DIM) / 100`, `PutImage` the result into
a server-side pixmap. That pixmap becomes the overlay's
`CW_BACK_PIXMAP`, so the X server paints the surround for free.
- **Override-redirect InputOutput window** covers the root. No window
manager involvement, no focus changes.
- **Spotlight**: SHAPE input region empty (clicks pass through), SHAPE
bounding region = ~564 per-row rectangles approximating a circle of
radius 140 px. A precomputed `circle_hw[]` table holds the horizontal
half-width per row (built in ~140 iterations at startup, then static).
Cursor tracking polls `XQueryPointer` at 30 Hz; on actual motion one
`SHAPE Rectangles` request rewrites the bound. Still cursor = zero
requests, zero CPU.
- **Draw**: GC on the pixmap with the user's colour, line-width, round
caps + joins. ButtonPress records the stroke origin; each Motion
event sends a `PolyLine` onto the pixmap and a `ClearArea` over the
segment's bounding box (padded for the round caps) to refresh just
that slice of the window.
- **Highlight**: same pixmap, no input pass-through (we own the
pointer). Drag rectangles trigger four-rect SHAPE bounding updates —
top, bottom, left, right bars around the user's hole, live as you
drag.
- **No keyboard grab.** Every key passes through to the focused
application underneath. The launch keybinding doubles as a kill —
`pkill -x spot || exec spot ...` toggles cleanly.
## Goals (CHasm rules in priority order)
1. **No wasted CPU.** Single SHAPE-rect request per cursor motion event.
No animation loops, no compositor wakeups.
2. **Lightning fast.** ~1 ms cold start (one syscall to connect, four to
create + map + grab + flush). Instantaneous show / hide.
3. **More battery.** Idle: not running. Active + still cursor: not
redrawing. Active + moving: one X request per ≥1 px delta.
## License
[Unlicense](https://unlicense.org/) - public domain.
## Credits
Created by Geir Isene (https://isene.org) with pair-programming via Claude Code.