Control Resolume Arena from your phone, tablet, or anywhere in the venue. No cables. No limitations. Just beautiful, responsive control.
Resolume's REST API is notoriously difficult. Most developers give up after:
- β Getting stuck on basic composition queries
- β Failing to control anything beyond layer 1
- β Realizing PUT/POST endpoints return 404s
- β Not understanding the undocumented parameter-by-ID pattern
The result? VJs are still chained to their laptops during performances.
I reverse-engineered the Resolume API and built the remote control that should have shipped with the software.
| Feature | Description |
|---|---|
| 4-Layer Control | Full access to layers 1-4, not just the first one |
| 9 Clips Per Layer | Trigger any clip with live thumbnail preview |
| Video Scrubbing | Drag the timeline, see it update instantly |
| BPM Sync | Match your visuals to the DJ's tempo |
| Opacity Control | Blend layers smoothly in real-time |
| Mobile-First | Responsive UI tested on iOS, Android, iPad |
| Multi-User | Each user connects to their own Resolume instance |
After extensive reverse-engineering, I discovered that Resolume's REST API requires a two-step pattern:
1. GET /composition β fetch parameter IDs
2. PUT /parameter/by-id/{id} β change values
This is undocumented. The official Swagger docs show endpoints that return 404. I discovered the real pattern through packet inspection and systematic testing.
One of the only TypeScript implementations of a Resolume controller:
// Fully typed clip model
interface ClipViewModel {
readonly id: number;
readonly name: string;
readonly isConnected: boolean;
readonly thumbnailUrl: string;
}
// Type-safe API client
class ResolumeApiClient {
async triggerClip(layer: LayerIndex, clip: ClipIndex): Promise<void>
async setLayerOpacity(layer: LayerIndex, opacity: number): Promise<void>
async setBpm(value: number): Promise<void>
}| Device | Experience |
|---|---|
| iPhone | Touch-optimized with 44px tap targets, no accidental triggers |
| iPad | Landscape mode transforms into a full control surface |
| Desktop | Hover states with keyboard shortcuts (roadmap) |
| Layer | Technology | Rationale |
|---|---|---|
| Frontend | React 18 + TypeScript | Type safety, modern hooks, excellent DX |
| Styling | Tailwind CSS | Rapid iteration, consistent design system |
| Backend | FastAPI (Python) | Async-first, auto-generated OpenAPI docs |
| Database | MongoDB | Flexible schemas for user configurations |
| Auth | JWT + bcrypt | Industry standard, stateless authentication |
| Payments | Stripe | Subscription billing infrastructure |
| Tunnel | ngrok | Secure localhost-to-cloud bridging |
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β USER DEVICES β
β π± iPhone π± Android π± iPad π» Desktop β
βββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββ
β HTTPS
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β RESOFLEUR CLOUD β
β βββββββββββββββ βββββββββββββββ βββββββββββββββ β
β β React ββββ FastAPI ββββ MongoDB β β
β β Frontend β β Backend β β Atlas β β
β βββββββββββββββ ββββββββ¬βββββββ βββββββββββββββ β
ββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββ
β Authenticated Proxy
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β NGROK TUNNEL β
βββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β RESOLUME ARENA/AVENUE β
β (Running locally on VJ's machine) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Key insight: Each user's Resolume stays on their machine. Resofleur provides secure remote access without exposing local networks.
- Resolume Arena/Avenue 7+ (REST API enabled)
- ngrok account (free tier)
- Node.js 18+, Python 3.11+
# Clone the repository
git clone https://github.com/yuan-cloud/resofleur.git
cd resofleur
# Backend
cd backend
pip install -r requirements.txt
uvicorn server:app --port 8001
# Frontend (new terminal)
cd frontend
yarn install
yarn start
# Enable Resolume REST API: Preferences β Webserver β Enable
# Start ngrok: ngrok http 8080
# Open http://localhost:3000 and add your ngrok URLresofleur/
βββ backend/
β βββ server.py # FastAPI application (~550 LOC)
β βββ models/ # Pydantic schemas
β β βββ user_models.py
β β βββ resolume_models.py
β β βββ payment_models.py
β βββ tests/ # pytest test suite
β
βββ frontend/
β βββ src/
β βββ components/ # React components
β βββ hooks/ # Custom hooks (connection, clips, controls)
β βββ context/ # Authentication context
β βββ services/ # Type-safe API client
β βββ pages/ # Route pages
β βββ types/ # TypeScript definitions
β
βββ README.md
- β JWT authentication with 24-hour token expiration
- β bcrypt password hashing (work factor 12)
- β User-scoped configurations (complete data isolation)
- β HTTPS enforced in production
- β Input validation via Pydantic models
- β CORS configured for production origins
- Keyboard shortcuts for clip triggering
- OSC protocol bridge
- Effect parameter controls
- Cue list / show programming
- Multi-composition switching
- Read the packets, not the docs β Resolume's Swagger documentation is incomplete
- Parameter IDs are everything β The undocumented key to actual control
- Mobile-first is mandatory β VJs need to move; the app should move with them
- Proxy architecture enables scale β User isolation becomes natural
Q: Why not use OSC?
A: OSC requires static IPs and network configuration. Resofleur works over the internet from anywhere.
Q: What's the latency?
A: ~50-100ms via ngrokβimperceptible for manual control.
Q: Can multiple people control one Resolume?
A: Yes. Share the same configuration for collaborative VJing.
Q: Which Resolume versions are supported?
A: Arena 7+ and Avenue 7+ with REST API enabled.
Proprietary software. All rights reserved.
Built by a full-stack engineer who actually VJs.
Because performers deserve better tools. πΈ