ShadowSpeak


A full-stack ESL learning platform where students practice pronunciation by recording themselves mimicking YouTube video segments, and teachers review submissions and provide feedback.

Live VersionRepo
ShadowSpeak app demo

Project Goal

Build a platform that replaces the scattered workflow ESL teachers use today: Google Drive for storage, email for submissions, and screen recording software for practice. Students can loop specific YouTube segments, record themselves in-browser, and submit directly. Teachers create lessons, assign them to students, and review submissions with feedback.

Tech Stack

  • Next.js 15 with App Router, React 19, TypeScript
  • Express.js backend with PostgreSQL database
  • Azure Blob Storage for audio recordings and images
  • JWT authentication with role-based access control
  • SWR for data fetching with optimistic updates
  • Material UI components, CSS Modules with container queries

Solo Project

This is my most comprehensive project. I built everything from scratch:

  • Browser audio recording using MediaRecorder API with start, pause, resume, and stop controls
  • YouTube segment looping via custom 100ms polling (YouTube API does not support native looping)
  • Cloud storage pipeline: recordings converted to base64, uploaded to Azure Blob Storage, URLs persisted to database
  • JWT auth with RBAC: teachers and students have different permissions enforced on both frontend and backend
  • PostgreSQL schema with users, lessons, and assignments tables, proper foreign keys and cascade deletes
  • State management using Context API + useReducer for complex recorder state machine

Solo Project

Technical Challenges Solved

YouTube Segment Looping

The YouTube embedded player does not support native segment looping. I built a custom solution using a useLoopButtons hook that polls the video position every 100ms. When playback reaches the end timestamp, it automatically seeks back to the start. The state machine manages transitions: idle, start_set, ready, looping.

Browser Audio Recording Pipeline

Recording audio in the browser and uploading to cloud storage required multiple integration steps: capture audio stream with MediaRecorder API, convert Blob to base64, send to Express backend, convert to Buffer, stream to Azure Blob Storage, return URL, and persist to PostgreSQL. All managed through a reducer pattern in RecorderPanelContext.

Role-Based Access Control

Implemented two-tier access: JWT tokens contain role claims ("teacher" or "student"), middleware validates every API request, and server-side route protection checks req.user.role !== "teacher" before allowing destructive operations. Students get 403 Forbidden if they try to access teacher endpoints.

What I Learned

  • How to build a complete full-stack application from database schema to deployed frontend
  • How to work with browser APIs (MediaRecorder) and cloud services (Azure Blob Storage)
  • How to implement proper authentication and authorization with JWT and role-based access
  • How to manage complex UI state using reducer patterns and state machines
  • How to design relational database schemas with proper foreign keys and constraints
  • How to handle file uploads with proper validation, size limits, and unique naming