Phroller Logo Phroller Companion API
Local: http://127.0.0.1:47321

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_fog with {"covered": true} or {"covered": false}.
  • Snap Camera to Boss Room: POST to /v1/set_camera with {"center": [20, 15], "zoom": 1.4}.
  • Open New Scene: POST to /v1/new_scene with {"name": "Ambush Site"}.
  • Undo Last Action: POST to /v1/undo_last with 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_boardReadRead the open map: grid type, levels, tokens, decals, walls, and doors.
screenshotReadCapture a PNG of the live board (longest edge 256–2048 px).
list_assetsReadList token, prop, and decal asset collections on the local machine.
search_libraryReadSearch the free online library of 3D models and map images.
import_assetWriteImport image, 3D model (GLB/GLTF), or PDF via URL, local path, or upload ID.
place_tokensWritePlace 3D models and character/prop tokens on grid cells.
place_decalsWriteLay map art, floor textures, rugs, text, or PDF handouts flat on the board.
draw_wallsWriteDraw polylines of walls, swinging doors, and full rectangular rooms.
update_objectsWriteMove, rotate, relabel, unlock, or delete tokens, decals, and doors.
set_levelsWriteAdd vertical levels/floors, set elevations, or change active floor.
set_fogWriteCover/reveal fog of war on the active or specified level.
set_environmentWriteConfigure grid style (square/hex), background, horizon, and sunlight.
set_cameraReadCenter camera on cell, set zoom level (0.2–5.0), yaw, and tilt pitch.
new_sceneWriteOpen a new map tab in the workspace.
undo_lastWriteRevert 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 #RRGGBB or 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.rolledtotal, dice: [{ die, value }], rollerFired whenever dice are rolled on the table.
tokens.movedtokens: [{ 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.changedactiveLevelActive floor changed or levels added/modified.
scene.switchedid, nameThe 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.');
});
Open in New Tab
Loading OpenAPI 3.1 specification...