Inspiration

In fast-paced lectures, the whiteboard changes quickly and students end up choosing between listening and copying. That tradeoff causes missed explanations, assignment details, and subtle exam hints. We wanted to remove that friction with a tool that works instantly, without logins, installs, or classroom hardware upgrades. The goal was simple: make the whiteboard followable in real time and make “important moments” hard to miss.

What it does

Boardcast is a no-login classroom companion where students enter a room code to join a live session. It delivers:

  • Live whiteboard streaming to remote viewers with low latency and minimal buffering.
  • Automatic whiteboard capture, continuously saving note flows so students never need to take photos.
  • Manual note saving, allowing students to capture important moments on demand.
  • Bookmarking & favorites, letting students mark key notes for quick review later.
  • File saving & sharing, allowing students to save notes locally as files or share them directly from the app.
  • Smart note filtering, with separate views for: all notes, auto-captured note, and favorited notes

How we built it

Backend and realtime

  • Django 5 + DRF in django/boardcast for rooms, media ingest, digitization, and health.
  • ASGI realtime: Django Channels + Daphne (for ASGI), with Redis as the channel layer and Celery broker/result backend, plus presence and transcript context.
  • DB: Postgres served by NeonDB with SQLite fallback,.

WebRTC and SFU signaling

  • Janus SFU (Videoroom) is the media plane. Django creates rooms via Janus HTTP API and returns janus_room_id and janus_url.
  • Signaling: Django server served as signaling server and relayed SDP and ICE JSON plus presence.
  • ICE: Google's STUN server and TURN, with coturn-style HMAC-signed TURN creds were used.

Async media and AI

  • Audio chunks to POST /api/media/audio-chunks/ are stored and queued.
  • Celery process runs ElevenLabs STT (Scribe v2) plus Gemini (gemini-2.5-flash) highlight detection, then broadcasts via WebSocket groups.

Deployment and frontend

  • Fly.io: Django ASGI app + Celery worker.
  • Janus on Fly.io as a separate app, with guidance to pin a public IP for ICE stability. Used Docker Compose for Redis + Janus and a coturn config placeholder.
  • Frontend: React 19 with client-side Next.js App Router consuming REST, WebSockets, and Janus. Client-only dynamic imports avoid SSR for camera/WebRTC pages. Core flows are host publish and participant subscribe, plus notes export (jsPDF, Web Share).

Challenges we ran into

  • Real-time scaling and NAT traversal: WebRTC was fine in small demos, but production-like reliability required SFU routing, correct ICE candidates, and careful handling of NAT and firewall behavior. Pinning a public IP for Janus and managing UDP RTP ranges mattered.

  • Signaling correctness under concurrency: Relaying SDP offers/answers and ICE candidates across multiple participants was sensitive to message ordering, reconnections, and half-connected states.

  • Latency budgeting across the full pipeline: Streaming, audio chunking, speech-to-text, and LLM highlight detection each added delay. Keeping notifications timely required an async pipeline with fast WebSocket fanout.

  • Signal versus noise in highlights: Defining “important moments” without spamming students required prompt iteration, thresholds, and pragmatic guardrails.

  • Heavy CV and ML constraints on free-tier infrastructure: We prototyped a Torch + YOLOv8-based pipeline to automatically remove the teacher from the whiteboard feed, but it was too compute-heavy to ship on free-tier cloud instances. To address this, we included a video showing how we would scale the vision workload with GPU-backed workers and a separate processing tier if pricing constraints were not a limit.

  • Operational constraints: Quotas and cost ceilings from external AI services shaped the MVP. We had to be intentional about what we process, when we process it, and how we degrade gracefully.

Accomplishments that we're proud of

Join-by-code with no login, scalable SFU-based WebRTC streaming, and a working async pipeline from audio chunks to transcripts to Gemini-based highlight detection and real-time alerts. We also shipped a deployable split-plane architecture on Fly.io with Django for signaling and APIs, and Janus for the media plane. We started with little to no WebRTC experience and quickly learned how challenging it is to combine real-time video delivery with heavy ML workloads, but we still delivered an end-to-end, multi-service streaming platform that worked.

What we learned

We learned how quickly a “single server, single database” mindset breaks down when you build real-time systems. Scaling live video beyond a demo required an SFU-based WebRTC architecture, reliable signaling, and careful networking considerations. At the same time, we built an end-to-end async pipeline where recorded audio chunks flow through multiple AI services, specifically ElevenLabs Scribe v2 for speech-to-text and Gemini for highlight detection, and still return results within a practical latency window. We also learned that ML-heavy computer vision, especially Torch-based workflows, is far more compute-intensive than we expected and often requires GPU-capable infrastructure, which can be difficult to justify on free-tier budgets.

What's next for Boardcast

Wire live whiteboard digitization into the core experience, add replay with timestamped highlights, improve highlight precision and user-tunable sensitivity, harden observability and reliability across WebRTC and async workers, and implement aggregated user interaction feature to show heatmaps of where students highlight the most.

Built With

Share this project:

Updates