Skip to content

Latest commit

Β 

History

46 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

JuggleIQ

CV-powered soccer juggling coach β€” upload, analyze, improve.

Built for Hacklytics 2026. Users upload a short juggling video; the system detects the ball, tracks pose, identifies touches, and returns metrics plus coaching tips. The frontend shows results with charts and exports session JSON for Figma Make dashboards.


Features

  • Upload video (MP4, MOV, AVI, MKV, WebM) and run analysis
  • Ball + pose pipeline: YOLO detection, Kalman smoothing, MediaPipe legs/feet
  • Touch detection: left/right foot, rhythm intervals, peak height, lateral drift
  • Knee stiffness proxy and coaching tips from the API
  • Dashboard: skill score, L/R foot donut, rhythm graph, score breakdown, touch timeline
  • Download session JSON for Figma Make or other tools
  • Anime.js entrance animations and subtle UI graphics

πŸŽ₯ System Demo Video

Watch the system in action:

Watch the demo


Full System Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     USER                                 β”‚
β”‚              (Mobile / Web Browser)                      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                      β”‚
                      β”‚ 1. Upload Video
                      β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                  FRONTEND                                β”‚
β”‚                                                         β”‚
β”‚   React / Flutter / Vue                                 β”‚
β”‚                                                         β”‚
β”‚   Pages:                                                β”‚
β”‚   β”œβ”€β”€ Upload Page     β†’ send video to API               β”‚
β”‚   β”œβ”€β”€ Loading Page    β†’ show progress spinner           β”‚
β”‚   β”œβ”€β”€ Dashboard Page  β†’ show all charts + results       β”‚
β”‚   └── Replay Page     β†’ show annotated video            β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                      β”‚
                      β”‚ 2. POST /analyze (video file)
                      β”‚ 3. GET  /download/{job_id} (video)
                      β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                  FASTAPI SERVER                          β”‚
β”‚              (main.py on Kaggle GPU)                     β”‚
β”‚                                                         β”‚
β”‚   Exposed via ngrok                                     β”‚
β”‚   https://xxxx.ngrok-free.app                           β”‚
β”‚                                                         β”‚
β”‚   Endpoints:                                            β”‚
β”‚   β”œβ”€β”€ GET  /health                                      β”‚
β”‚   β”œβ”€β”€ POST /analyze      β†’ runs VideoProcessor          β”‚
β”‚   β”œβ”€β”€ GET  /download/{id}β†’ returns annotated video      β”‚
β”‚   └── DELETE /cleanup/{id}                              β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                      β”‚
                      β”‚ 4. process_video()
                      β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚               VIDEO PROCESSOR                            β”‚
β”‚             (video_processor.py)                         β”‚
β”‚                                                         β”‚
β”‚   Frame by Frame Pipeline:                              β”‚
β”‚                                                         β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚   β”‚  YOLO   │───▢│ Kalman  │───▢│  Ball History   β”‚   β”‚
β”‚   β”‚ detect  β”‚    β”‚ filter  β”‚    β”‚  + Trail        β”‚   β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                           β”‚             β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                     β”‚             β”‚
β”‚   β”‚    MediaPipe    │───▢ Pose Landmarks  β”‚             β”‚
β”‚   β”‚  Pose Estimator β”‚                     β”‚             β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                     β”‚             β”‚
β”‚                                           β–Ό             β”‚
β”‚                               β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
β”‚                               β”‚  Touch Detection  β”‚    β”‚
β”‚                               β”‚  F1: Foot Label   β”‚    β”‚
β”‚                               β”‚  F2: Proximity    β”‚    β”‚
β”‚                               β”‚  F3: Peak+Drift   β”‚    β”‚
β”‚                               β”‚  F4: Knee Angle   β”‚    β”‚
β”‚                               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
β”‚                                         β”‚               β”‚
β”‚                                         β–Ό               β”‚
β”‚                               β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
β”‚                               β”‚   get_results()   β”‚    β”‚
β”‚                               β”‚   returns JSON    β”‚    β”‚
β”‚                               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                      β”‚
                      β”‚ 5. Returns JSON + annotated video
                      β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                  FRONTEND DASHBOARD                      β”‚
β”‚                                                         β”‚
β”‚  JSON data mapped to:                                   β”‚
β”‚  β”œβ”€β”€ Skill Score Ring      ← skill_score                β”‚
β”‚  β”œβ”€β”€ L vs R Donut Chart    ← left_pct / right_pct       β”‚
β”‚  β”œβ”€β”€ Rhythm Line Graph     ← intervals[]                β”‚
β”‚  β”œβ”€β”€ Foot Timeline         ← touches[].foot + t         β”‚
β”‚  β”œβ”€β”€ Height Heatmap        ← touches[].x/y + peak       β”‚
β”‚  β”œβ”€β”€ Drift Channel         ← avg_drift_px               β”‚
β”‚  β”œβ”€β”€ Knee Feedback Card    ← knee_feedback{}            β”‚
β”‚  β”œβ”€β”€ Coaching Tips List    ← coaching_tips[]            β”‚
β”‚  └── Annotated Video       ← /download/{job_id}         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Data Flow (Step by Step)

  1. User picks a video on phone or browser.
  2. Frontend sends POST /analyze with the video file.
  3. FastAPI receives it and saves to /kaggle/working/videos/.
  4. video_processor.py runs frame by frame:
    • YOLO detects the ball every frame
    • Kalman filter smooths and fills gaps
    • MediaPipe detects leg skeleton
    • Touch detection runs on detected frames
    • Overlays are drawn on each frame
  5. get_results() builds the final JSON.
  6. FastAPI returns the JSON to the frontend.
  7. Frontend renders the dashboard from the JSON.
  8. User can click β€œWatch Replay” (or open annotated video link).
  9. Frontend calls GET /download/{job_id}.
  10. Annotated video streams back to the browser.

Technology Choices

Layer What Why
CV Engine YOLO + MediaPipe + Kalman Ball + pose detection
GPU Server Kaggle T4 Free GPU
API Layer FastAPI Fast, async, auto docs
Tunnel ngrok Expose Kaggle publicly
Frontend React + Vite SPA, fast dev experience
Charts Recharts Rhythm, foot donut, radar
Animation Anime.js Entrance and UI motion
Video HTML5 <video> Replay annotated video

Frontend Pages

Page 1 β€” Upload

  • [ Choose Video ] button (or β€œChoose video file” label)
  • On select β†’ POST /analyze
  • Loading spinner while the API processes

Page 2 β€” Dashboard (from JSON)

Block Data source
Skill: 55.4 skill_score
L:80% R:20% left_pct / right_pct
Rhythm Graph intervals[]
Foot Timeline touches[].foot + t
Height / Drift touches[].x/y, avg_drift_px
Knee: 175Β° Stiff knee_feedback
Coaching Tips coaching_tips[]
Watch Replay GET /download/{job_id}

Layout sketch:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Skill: 55.4  β”‚  L:80% R:20% β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Rhythm Graph                β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Foot Timeline   L─R─L─L─L   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Height Map   β”‚ Drift: 26px  β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Knee: 175Β° Very Stiff       β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Tips: β†’ Chain more touches  β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ [ β–Ά Watch Replay ]          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Tech Stack Summary

Layer Technologies
Backend Python 3.10+, FastAPI, OpenCV, Ultralytics YOLO, MediaPipe, NumPy
Frontend React 19, Vite 7, Recharts, Anime.js
Hosting Kaggle (backend), ngrok (public URL), static frontend (e.g. Vercel/Netlify)

Getting Started

Backend (Kaggle + ngrok)

  • Run the FastAPI app (cv/main.py) in a Kaggle notebook or environment where video_processor.py and models (YOLO, MediaPipe pose landmarker) are available.
  • Expose the server with ngrok and set the frontend API base URL to that ngrok URL.

Frontend

cd frontend
npm install
npm run dev
  • Set VITE_API_BASE in .env if your API is not at the default ngrok URL.
  • Production build: npm run build β†’ deploy the dist/ folder.

API base URL

  • Configure in frontend/src/config.js or via VITE_API_BASE (e.g. https://your-ngrok-subdomain.ngrok-free.dev).

Repository Structure

JuggleIQ/
β”œβ”€β”€ cv/
β”‚   β”œβ”€β”€ main.py              # FastAPI app: /health, /analyze, /download, /cleanup
β”‚   └── video_processor.py   # YOLO, MediaPipe, Kalman, touch detection, get_results()
β”œβ”€β”€ frontend/
β”‚   β”œβ”€β”€ src/
β”‚   β”‚   β”œβ”€β”€ App.jsx          # Upload, results, charts, Figma Make block
β”‚   β”‚   β”œβ”€β”€ config.js        # API_BASE
β”‚   β”‚   └── App.css
β”‚   └── public/samples/      # Sample drill videos
β”œβ”€β”€ drills/                  # Source drill videos
β”œβ”€β”€ docs/
β”‚   β”œβ”€β”€ PRD.md
β”‚   β”œβ”€β”€ tech_stack.md
β”‚   └── DEVPOST_OVERVIEW.md
β”œβ”€β”€ requirements.txt         # Python backend deps
└── README.md                # This file

API Endpoints

Method Path Description
GET /health Health check
POST /analyze Upload video β†’ analysis JSON
GET /download/{id} Annotated video (inline)
DELETE /cleanup/{id} Remove annotated video file

Figma Make

Session JSON from Download session JSON is under 5MB and can be imported as a Figma Make dataset. Use it to build animated dashboards (skill gauge, foot donut, touch timeline, knee badge, coaching tips). See docs/DEVPOST_OVERVIEW.md for animation ideas and workflow.


License & Credits

  • JuggleIQ β€” Hacklytics 2026
  • CV: YOLO (Ultralytics), MediaPipe
  • Frontend: React, Vite, Recharts, Anime.js

πŸ‘₯ Team

Gabriel Dos Santos β€” MediaPipe integration Gavin Calleja β€” FigmaMake Dashboard Analytics

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages