Skip to main content

The paintbrush for Claude. A visual output surface for AI agents — diagrams, tables, images, and interactive controls.

Project description

lux

A visual output surface for AI agents.

License CI PyPI Python

Lux gives AI agents a window they can draw into. It runs an ImGui display server on the local machine, connected by Unix socket IPC. Agents send JSON element trees via MCP tools; the display renders them at 60fps. The protocol is the API surface --- if an agent can describe it as JSON, Lux renders it.

The design follows Smalltalk's Morphic model: every visible element is a composable, nestable object. Windows contain tabs, tabs contain groups, groups contain buttons and plots. The long-term goal is a live environment where the MCP server is the message bus and Lux is the rendering layer, with the agent as the programmer at the keyboard.

Platforms: macOS, Linux

Stage: alpha --- protocol is stable, published on PyPI as punt-lux

Quick Start

uv pip install -e .        # Install from source
lux display &              # Start the display server
lux serve                  # Start the MCP server (stdio)
Run a demo
lux display &
uv run python demos/dashboard.py

Demos are in demos/ --- each connects as a client and drives the display:

Demo What it shows
interactive.py Sliders, checkboxes, combos, text inputs, color pickers
containers.py Windows, tab bars, collapsing headers, groups
dashboard.py Multi-window layout with draw canvases and live controls
data_viz.py Tables, plots, progress bars, spinners, markdown
menu_bar.py Custom menus, event handling, periodic refresh

Features

  • 22 element kinds --- text, buttons, images, sliders, checkboxes, combos, inputs, radios, color pickers, selectables, trees, tables, plots, progress bars, spinners, markdown, draw canvases, groups, tab bars, collapsing headers, windows, separators
  • Layout nesting --- windows contain tab bars contain groups contain any element, arbitrarily deep
  • Incremental updates --- update patches individual elements by ID without replacing the scene
  • Menu bar --- built-in Lux/Theme/Window menus, plus agent-extensible custom menus via set_menu
  • Interaction events --- button clicks, slider changes, menu selections queue as events the agent reads via recv
  • Auto-spawn --- LuxClient starts the display server on first connection if it isn't running
  • Unix socket IPC --- length-prefixed JSON frames, no HTTP overhead, no threads

MCP Tools

Agents interact with Lux through six MCP tools exposed by lux serve:

Tool What it does
show(scene_id, elements) Replace the display with a new element tree
update(scene_id, patches) Patch elements by ID (set fields or remove)
set_menu(menus) Add custom menus to the menu bar
clear() Remove all content from the display
ping() Round-trip latency check
recv(timeout) Read the next interaction event (clicks, changes)

What It Looks Like

Show text and a button

{"tool": "show", "input": {
  "scene_id": "hello",
  "elements": [
    {"kind": "text", "id": "t1", "content": "Hello from the agent"},
    {"kind": "button", "id": "b1", "label": "Click me"}
  ]
}}

Returns "ack:hello". When the user clicks the button:

{"tool": "recv", "input": {"timeout": 5.0}}

Returns "interaction:element=b1,action=click,value=True".

Multi-window dashboard

{"tool": "show", "input": {
  "scene_id": "dash",
  "elements": [
    {"kind": "window", "id": "w1", "title": "Controls", "x": 10, "y": 10,
     "children": [
       {"kind": "slider", "id": "vol", "label": "Volume", "value": 50}
     ]},
    {"kind": "window", "id": "w2", "title": "Chart", "x": 320, "y": 10,
     "children": [
       {"kind": "plot", "id": "p1", "title": "Trend",
        "series": [{"label": "y", "type": "line",
          "x": [1,2,3,4], "y": [10,20,15,25]}]}
     ]}
  ]
}}

Update a single element

{"tool": "update", "input": {
  "scene_id": "dash",
  "patches": [
    {"id": "vol", "set": {"value": 75}}
  ]
}}

Element Kinds

Category Kinds
Display text, button, image, separator
Interactive slider, checkbox, combo, input_text, radio, color_picker
Lists selectable, tree
Data table, plot, progress, spinner, markdown
Canvas draw (line, rect, circle, triangle, polyline, text, bezier)
Layout group, tab_bar, collapsing_header, window

All elements with an id support an optional tooltip field (string shown on hover).

CLI Commands

Command What it does
lux display Start the display server (ImGui window)
lux serve Start the MCP server (stdio transport)
lux status Check if the display server is running
lux version Print version

Architecture

Agent (Claude Code)
  │ MCP (stdio)
  ▼
lux serve (FastMCP)
  │ Unix socket (JSON frames)
  ▼
lux display (ImGui + OpenGL)
  │ renders at 60fps
  ▼
Window on screen

The display server and MCP server are separate processes. The MCP server is a thin adapter that translates MCP tool calls into protocol messages sent over the Unix socket. The display server runs the ImGui render loop, polls the socket each frame via select() with zero timeout, and renders whatever scene the agent last sent.

Client code can also use LuxClient directly as a Python library, bypassing MCP. The demos do this.

Documentation

Design Log | Changelog | Contributing

Development

uv sync --extra dev            # Install dependencies
uv run ruff check .            # Lint
uv run ruff format --check .   # Check formatting
uv run mypy src/ tests/        # Type check (mypy)
uv run pyright                 # Type check (pyright)
uv run pytest                  # Test

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

punt_lux-0.5.0.tar.gz (41.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

punt_lux-0.5.0-py3-none-any.whl (44.9 kB view details)

Uploaded Python 3

File details

Details for the file punt_lux-0.5.0.tar.gz.

File metadata

  • Download URL: punt_lux-0.5.0.tar.gz
  • Upload date:
  • Size: 41.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for punt_lux-0.5.0.tar.gz
Algorithm Hash digest
SHA256 b0bc357ee85fe72677bf4637a87ca327210e0a1ea6bf7529b6414dc1291ade03
MD5 a7a68ddad004fc3f92c93f162c3c37d6
BLAKE2b-256 9ff7475ca57fd4453aa5c462e27f4305fe96049430d02656955b7722d1952bef

See more details on using hashes here.

Provenance

The following attestation bundles were made for punt_lux-0.5.0.tar.gz:

Publisher: release.yml on punt-labs/lux

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file punt_lux-0.5.0-py3-none-any.whl.

File metadata

  • Download URL: punt_lux-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 44.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for punt_lux-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a14e1685bb1c2b8da53d4dad6c5e6df38ff581f65db49188ecd37dbf4c175949
MD5 b8405f3ec085983b6db3bd2ce3a92fea
BLAKE2b-256 1860c96af26c174eb6c5145cc552702c0f2410ae1d338bad89d29fc798c07397

See more details on using hashes here.

Provenance

The following attestation bundles were made for punt_lux-0.5.0-py3-none-any.whl:

Publisher: release.yml on punt-labs/lux

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page