Scripts, Tools & Automation
The Phroller Companion API runs a local HTTP REST and WebSocket server at http://127.0.0.1:47321. Any programming language, macro keypad, or broadcast tool can interact with your live tabletop session.
1. Python: Procedural Room & Dungeon Generator
Automate map preparation with simple HTTP requests. For example, draw a complete stone room with doorways and place character tokens:
import requests
API = "http://127.0.0.1:47321/v1"
HEADERS = {"Authorization": "Bearer <key>"}
# 1. Draw a 10x8 dungeon room with a south door
requests.post(f"{API}/draw_walls", json={
"rooms": [{
"rect": [0, 0, 10, 8],
"height": 2,
"color": "#718096",
"doors": [{"side": "south", "at": 4, "width": 1}]
}]
}, headers=HEADERS)
# 2. Place character tokens inside the room
requests.post(f"{API}/place_tokens", json={
"tokens": [
{"asset": "fighter_mini", "cell": [3, 4], "label": "Thorin"},
{"asset": "goblin_mini", "cell": [7, 3], "label": "Goblin Scout"}
]
}, headers=HEADERS)
2. Elgato Stream Deck & Macro Pads
Map physical keys to trigger immediate board changes during a game session:
- Toggle Fog of War: POST to
/v1/set_fogwith{"covered": true}or{"covered": false}. - Snap Camera to Boss Room: POST to
/v1/set_camerawith{"center": [20, 15], "zoom": 1.4}. - Open New Scene: POST to
/v1/new_scenewith{"name": "Ambush Site"}. - Undo Last Action: POST to
/v1/undo_lastwith empty JSON{}.
# 1-click Stream Deck curl command to uncover the active map
curl -X POST http://127.0.0.1:47321/v1/set_fog \
-H "Authorization: Bearer <key>" \
-H "Content-Type: application/json" \
-d '{"covered": false}'
3. OBS Browser Source & Live Stream Overlays
Connect any OBS browser source to ws://127.0.0.1:47321/v1/events?token=<key> to display live dice rolls on your broadcast:
const token = 'YOUR_API_KEY';
const ws = new WebSocket(`ws://127.0.0.1:47321/v1/events?token=${encodeURIComponent(token)}`);
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (msg.type === 'dice.rolled') {
const roller = msg.roller || 'Player';
document.getElementById('stream-banner').innerText = `🎲 ${roller} rolled ${msg.total}!`;
}
};
4. Exposed Board Commands
| Command | Type | Summary |
|---|---|---|
get_board | Read | Read the open map: grid type, levels, tokens, decals, walls, and doors. |
screenshot | Read | Capture a PNG of the live board (longest edge 256–2048 px). |
list_assets | Read | List token, prop, and decal asset collections on the local machine. |
search_library | Read | Search the free online library of 3D models and map images. |
import_asset | Write | Import image, 3D model (GLB/GLTF), or PDF via URL, local path, or upload ID. |
place_tokens | Write | Place 3D models and character/prop tokens on grid cells. |
place_decals | Write | Lay map art, floor textures, rugs, text, or PDF handouts flat on the board. |
draw_walls | Write | Draw polylines of walls, swinging doors, and full rectangular rooms. |
update_objects | Write | Move, rotate, relabel, unlock, or delete tokens, decals, and doors. |
set_levels | Write | Add vertical levels/floors, set elevations, or change active floor. |
set_fog | Write | Cover/reveal fog of war on the active or specified level. |
set_environment | Write | Configure grid style (square/hex), background, horizon, and sunlight. |
set_camera | Read | Center camera on cell, set zoom level (0.2–5.0), yaw, and tilt pitch. |
new_scene | Write | Open a new map tab in the workspace. |
undo_last | Write | Revert the last change made by the API without affecting user edits. |
5. Coordinates & Geometry Reference
- Grid Cells: Coordinate
[x, y]where tokens stand. 1 cell = 60 world pixels. - Points: Coordinate
[x, y]at cell corners. Walls, doors, and bounding rectangles run on points. For example, a room covering cells (0,0) to (4,3) has rectangle[0, 0, 5, 4]. - Hex Maps: Pointy-top offset coordinates where odd rows are shifted half a cell. Points remain a square lattice.
- Colors: Hex colors formatted as
#RRGGBBor with alpha#AARRGGBB.
6. Optional: Model Context Protocol (MCP) Adapter
For developers experimenting with agentic scripting or IDE tools, Phroller also provides an optional Streamable HTTP MCP adapter at http://127.0.0.1:47321/mcp. All board commands above are exposed as MCP tools:
# Connect with Claude Code CLI
claude mcp add --transport http phroller http://127.0.0.1:47321/mcp \
--header "Authorization: Bearer <key>"
Real-time WebSocket Events
Stream real-time dice rolls, token movements, map state changes, and scene switches directly to your stream overlays, bots, or companion screens.
Connection Protocol
Connect a WebSocket client to ws://127.0.0.1:47321/v1/events. If your client cannot set an Authorization: Bearer <key> header (e.g. browser WebSocket API), supply the token in the query string:
ws://127.0.0.1:47321/v1/events?token=<key>
Upon connection, the server immediately returns a welcome handshake:
{
"type": "hello",
"apiVersion": "1"
}
Event Catalog
| Event Type | Payload Fields | Description |
|---|---|---|
dice.rolled | total, dice: [{ die, value }], roller | Fired whenever dice are rolled on the table. |
tokens.moved | tokens: [{ id, cell: [x,y], level }] | Tokens were dragged or repositioned. Coalesced to several times a second during dragging. |
tokens.changed | (none; query /v1/board) | Token properties, labels, locks, or deletion changed. |
decals.changed | (none; query /v1/board) | Decal artwork, position, or opacity modified. |
strokes.changed | (none; query /v1/board) | Walls, doors, or freehand drawing strokes modified. |
fog.changed | (none; query /v1/board) | Fog of war coverage or reveal zones updated. |
levels.changed | activeLevel | Active floor changed or levels added/modified. |
scene.switched | id, name | The GM switched map tabs. |
Browser / Node.js Client Example
const token = 'YOUR_API_KEY';
const socket = new WebSocket(`ws://127.0.0.1:47321/v1/events?token=${encodeURIComponent(token)}`);
socket.addEventListener('open', () => {
console.log('Connected to Phroller Companion Events!');
});
socket.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
console.log('Received Phroller event:', data.type, data);
if (data.type === 'dice.rolled') {
console.log(`Rolled ${data.total}!`, data.dice);
} else if (data.type === 'tokens.moved') {
console.log('Tokens moved:', data.tokens);
}
});
socket.addEventListener('close', () => {
console.log('Disconnected from Phroller Companion Events.');
});
Loading OpenAPI 3.1 specification...