Interactive Workshop Site
When to use
This skill applies when the core interaction model is one controller + many followers + real-time sync:
- Corporate training / workshops
- Classroom teaching tools
- Conference / summit interactive platforms
- Quiz shows / game shows
- Live event interaction (polls, Q&A, voting)
For general websites without this model, use individual skills instead.
Composable skills
This skill orchestrates these foundational skills — read each for detailed patterns:
| Skill | Purpose |
|---|---|
nextjs-app-scaffold |
Project structure, App Router, custom server |
realtime-state-sync |
Socket.io state management, admin auth |
dark-glass-ui |
Visual design system (or substitute your own) |
railway-docker-deploy |
Containerized deployment |
video-subtitle-pipeline |
Video content with subtitles (if needed) |
Invoke with: @~/.cursor/skills/nextjs-app-scaffold etc.
Architecture overview
┌─────────────┐ ┌───────────────────────────────────────┐
│ Facilitator │ ──────▶ │ server.mjs │
│ (Admin UI) │ admin:* │ ┌───────────────────────────────┐ │
└─────────────┘ │ │ State (in-memory) │ │
│ │ - currentStage │ │
┌─────────────┐ │ │ - timer │ │
│ Participant │ ◀─────▶ │ │ - teams (members, game data) │ │
│ (Client) │ state:* │ │ - polls (votes, results) │ │
└─────────────┘ │ └───────────────────────────────┘ │
│ Next.js App Router │
┌─────────────┐ │ Socket.io Server │
│ Participant │ ◀─────▶ │ │
│ (Client) │ └───────────────────────────────────────┘
└─────────────┘
Core concept: Stage-driven finite state machine
The workshop is a linear sequence of stages. The facilitator advances stages from the admin panel; all participants see the current stage's content.
// src/lib/types.ts
export interface WorkshopSyncState {
currentStage: number;
eventDate: string;
eventTime: string;
timer: { running: boolean; remaining: number; total: number };
teams: Record<TeamId, TeamState>;
polls: Record<string, PollState>;
}
Build sequence
Phase 1: Foundation
- Scaffold project →
@~/.cursor/skills/nextjs-app-scaffold - Set up Socket.io →
@~/.cursor/skills/realtime-state-sync - Define
WorkshopSyncStateinterface with all stage/team/poll fields - Implement admin authentication
Phase 2: Content & interaction
- Define stages in
src/lib/workshop-data.ts(title, description, video, games) - Build stage display page (
src/app/stage/[id]/page.tsx) - Build admin control panel (
src/app/admin/page.tsx):- Stage navigation (prev/next)
- Timer controls (start/pause/reset)
- Poll management (open/close/show results)
- Game phase controls
Phase 3: Interactive features
- Team system: Join page, team assignment, member tracking
- Live polls: Real-time voting with result visualization
- Games: Stage-specific interactive challenges
- Timer: Server-authoritative countdown displayed on all clients
Phase 4: Polish & deploy
- Apply design system →
@~/.cursor/skills/dark-glass-ui - Process videos →
@~/.cursor/skills/video-subtitle-pipeline(if needed) - Deploy →
@~/.cursor/skills/railway-docker-deploy
Key pages
| Route | Purpose | Access |
|---|---|---|
/ |
Landing page with stage overview | Public |
/access |
Access gate (password/code entry) | Public |
/join |
Team selection & member sign-in | Participants |
/stage/[id] |
Stage content, video, games | Participants |
/finish |
Completion page, certificate | Participants |
/admin |
Facilitator control panel | Admin only |
/presenter |
Projector-optimized view | Admin |
Admin panel capabilities
The admin panel is the facilitator's command center:
- Stage control: Navigate between stages
- Timer: Set duration, start, pause, resume, reset
- Polls: Open/close voting, show/hide results
- Games: Trigger game phases, view submissions, show results
- Event info: Customize date, time, and other display metadata
- Reset: Per-stage reset or full workshop reset
Team data flow
Participant joins → team:join → server adds to team → broadcastState
Participant submits game → game1:submit → server stores → broadcastState
Admin views results → admin:getFullState → server sends full data
Teams hold both public data (member count, names) and private data (game submissions). Only public data is included in state:sync; private data is sent only to admin via separate events.
Content data pattern
All workshop content lives in data files, not components:
// src/lib/workshop-data.ts
export const stages: StageInfo[] = [
{
id: 1,
title: "Stage Title",
subtitle: "Brief description",
videoUrl: "/videos/stage1.mp4",
videoEnd: 180,
games: ["game-component-name"],
polls: [1],
},
// ...
];
export const pollQuestions: PollQuestion[] = [ /* ... */ ];
Components import data and render it. This makes content changes trivial without touching component logic.
Checklist
- Project scaffolded with custom server.mjs
- Socket.io state machine with all workshop fields
- Admin auth and control panel functional
- Stages defined in data file, rendered dynamically
- Team join flow working
- Polls: vote → aggregate → display results
- Timer: server-authoritative, synced to all clients
- Games: submit → store → admin review → show results
- Design system applied consistently
- Deployed and tested with multiple simultaneous clients