Skip to content

lauragift21/slide-reactions

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 

Repository files navigation

slide-reactions

Live emoji reactions for slide presentations, powered by Cloudflare Workers and Durable Objects.

Add real-time, audience-driven emoji reactions to any presentation framework. Audience members tap an emoji, and every connected viewer sees the count update instantly over WebSockets.

How It Works

┌──────────────┐   WebSocket    ┌─────────────────────┐
│   Browser A   │──────────────▶│  Cloudflare Worker   │
│  (presenter)  │◀──────────────│                      │
└──────────────┘                │  ┌─────────────────┐ │
                                │  │  Durable Object  │ │
┌──────────────┐   WebSocket    │  │  (per slide)     │ │
│   Browser B   │──────────────▶│  │                  │ │
│  (audience)   │◀──────────────│  │  SQLite storage  │ │
└──────────────┘                │  └─────────────────┘ │
                                └─────────────────────┘
  1. Each slide gets its own Durable Object instance, identified by the slide ID.
  2. Clients connect via WebSocket to /ws/:slideId — the Worker routes the connection to the correct Durable Object.
  3. When a user reacts, the client sends { type: "react", emoji: "👍" } over the WebSocket.
  4. The Durable Object increments the count in its SQLite database and broadcasts the update to every connected client.
  5. On initial connection, the server sends the full reaction counts so late-joiners see the current state.

Rate limiting (10 reactions per 5-second window per client) and emoji validation are handled server-side.

Project Structure

├── reactions-worker/        # Cloudflare Worker backend
│   └── src/
│       ├── index.ts         # HTTP router (WebSocket upgrade, REST, CORS)
│       └── reactions-do.ts  # Durable Object (WebSocket handling, SQLite storage)
│
└── slide-reactions/         # npm packages (monorepo)
    └── packages/
        ├── slide-reactions/                  # Client library + React components
        │   └── src/
        │       ├── client.ts                 # Framework-agnostic WebSocket client
        │       ├── hooks/useReactions.ts     # React hook
        │       ├── components/ReactionBubble.tsx  # Drop-in React component
        │       └── types.ts                  # Shared TypeScript types
        │
        └── create-slide-reactions-worker/    # CLI scaffolding tool
            ├── src/index.ts                  # npx create script
            └── template/                     # Worker template files

Getting Started

1. Deploy the Worker Backend

cd reactions-worker
npm install
npm run dev      # Local development on :8787
npm run deploy   # Deploy to Cloudflare Workers

The worker exposes:

Route Description
GET /ws/:slideId WebSocket upgrade — connects to the Durable Object for that slide
GET /reactions/:slideId REST endpoint — returns current reaction counts as JSON
GET / or GET /health Health check

2. Add Reactions to Your Frontend

Install the client library:

npm install slide-reactions

Option A: React Component (quickest)

import { ReactionBubble } from "slide-reactions";

function App() {
  return (
    <ReactionBubble
      serverUrl="wss://your-worker.workers.dev"
      slideId="slide-0"
    />
  );
}

The <ReactionBubble> renders a floating button that expands into an emoji reaction panel with live counts, sound feedback, and connection status.

Option B: React Hook (custom UI)

import { useReactions } from "slide-reactions";

function CustomReactions({ slideId }: { slideId: string }) {
  const { counts, react, isConnected } = useReactions({
    serverUrl: "wss://your-worker.workers.dev",
    slideId,
  });

  return (
    <div>
      {Object.entries(counts).map(([emoji, count]) => (
        <button key={emoji} onClick={() => react(emoji)}>
          {emoji} {count}
        </button>
      ))}
    </div>
  );
}

Option C: Vanilla JavaScript Client

import { SlideReactionsClient } from "slide-reactions/client";

const client = new SlideReactionsClient({
  serverUrl: "wss://your-worker.workers.dev",
  slideId: "slide-0",
  onCountsUpdate: (counts) => console.log(counts),
  onReaction: (emoji, count) => console.log(`${emoji}: ${count}`),
});

client.connect();
client.react("👍");

3. Or Scaffold a New Worker

If you want to spin up your own instance of the backend:

npx create-slide-reactions-worker my-reactions
cd my-reactions
npm install
npm run dev

Configuration

Worker Environment Variables

Variable Description Default
ALLOWED_ORIGINS Comma-separated list of allowed CORS origins *

Set via wrangler.jsonc vars or Cloudflare dashboard secrets.

Client Options

Prop / Option Type Default Description
serverUrl string WebSocket server URL (required)
slideId string Current slide identifier (required)
emojis string[] ["👀", "👍", "❤️", "🤯"] Available emoji reactions
maxReconnectAttempts number Infinity Max auto-reconnect attempts

ReactionBubble Props

All client options above, plus:

Prop Type Default Description
position "bottom-right" | "bottom-left" | "top-right" | "top-left" "bottom-right" Position of the floating bubble
sound boolean true Play a pop sound on reaction
className string Additional CSS class
style CSSProperties Additional inline styles

Technical Details

  • Durable Objects with Hibernation API — WebSocket connections are managed efficiently; the DO hibernates when idle and wakes on incoming messages.
  • SQLite storage — Reaction counts persist in the Durable Object's built-in SQLite database. Each emoji is a row with a count.
  • Rate limiting — Sliding window (10 reactions per 5 seconds per WebSocket connection) prevents spam.
  • Reconnection with exponential backoff — The client automatically reconnects (1s, 2s, 4s, ... up to 30s) on disconnection.
  • Slide switching — Calling changeSlide() or updating the slideId prop cleanly disconnects from the old slide and connects to the new one, resetting counts.

Development

# Worker
cd reactions-worker
npm install
npm run dev

# Client library (watch mode)
cd slide-reactions
npm install
npm run dev

License

MIT

About

Real-time slide reactions with Cloudflare Workers and React

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors