Mikiri (見切り, "reading the shuttle") is a local video analysis app for badminton, formerly JumpLab. It covers jumps, lunges and recovery; court position and footwork; rallies and split steps; racket and stroke description; and coaching advice built on all of it. What is still to come is in docs/feature-roadmap.md.
Local video biomechanics for badminton movements. Load a phone clip, track the athlete with RTMPose on the CPU, scrub frame by frame with a skeleton overlay, read joint angles over time, mark takeoff and landing (or let Mikiri suggest them), fix bad keypoints by dragging them, and export a self-describing pack you can hand to a person or an AI assistant. No cloud, no telemetry; the only network access is the one-off model download.
Mikiri is a single-camera 2D tool: it measures angles in the image plane from a side-on view. See "Limitations" before trusting a number.
Requirements: Python 3.11 or newer on Windows, macOS or Linux. ffmpeg is
bundled (via imageio-ffmpeg), nothing else to install.
pip install -e .mikiriThe browser opens at http://127.0.0.1:8765. Then:
- Open video: the system's own file dialog opens. On a machine with no display to put one on, paste a path or upload instead. Duration, fps and resolution appear above the frame; rotation metadata is respected.
- Trim: drag the blue handles on the timeline or press I / O on the current frame.
- Run tracking: click the athlete in the first frame (or "Track largest person"). The first run downloads the models (about 150 MB) and tells you where from. Progress bar and Cancel are in the dialog. Once the court is calibrated the dialog also offers the player at the other end: they are told apart by which side of the net they stand on and drawn in grey on the video and the court map, and their contacts feed the rally chain. For doubles tick "Two players on each side": partners are told apart by continuity, and the frames where they overlap are marked rather than guessed. The line under the transport says what to do next -- track, calibrate the court, track the other end, answer the review, save -- and the right-hand column is four pages: Movement, Match, Over time and Project. A page that has nothing yet says what would fill it.
- Scrub: Left/Right one frame, Shift+arrows ten frames, Space play/pause at 0.1x to 1x. Click any plot or the timeline to jump.
- Events: press a preset (split step, deepest load, takeoff, contact, landing, recovered) to mark the current frame, or "Auto-detect takeoff / landing" and accept the suggestions. Flight time appears immediately.
- Fix keypoints: tick "Edit keypoints" (E), drag a joint, optionally "Interpolate between" two corrected frames, undo with Ctrl+Z.
- Export pack: choose whether to include the videos, then find the
folder and zip in the dialog. Save (Ctrl+S)
writes
<video>.mikiri.jsonnext to the clip; opening the clip again restores everything.
demo/ holds one clip: demo.mp4, a synthetic jump the tests use, which
mikiri make-demo regenerates. Real footage lives in benchmarks/ and is
described by benchmarks/clips.json -- a phone recording of jump smashes from
behind-diagonal, a screen recording of a match seen from behind, a side-on
smash and a Lin Dan rally. The videos themselves are never committed; see
benchmarks/README.md. Real clips contain other players:
use the "Track largest person" default or click the athlete in the Run dialog,
and expect to flip "Swap L/R" for footage from behind.
-
Skeleton overlay colour-coded left (green) / right (orange), with a "Swap L/R" toggle for clips filmed from behind.
-
Plots of knee, hip, elbow, shoulder and ankle angles (left and right), trunk lean, segment angles versus the ground, hip height, ankle heights and stance width, with a cursor synced to the scrubber and event lines.
-
Movement profiles: jump smash, scissor-kick smash, block jump, lunge, split step and recovery. The profile sets the event buttons, the automatic suggestions (flight, hop or lunge detectors) and an Assessment table that compares the clip's metrics with reference bands and gives a cue when a value is outside its band. Bands are coaching heuristics; edit them in
movements.jsonin the Mikiri folder. See docs/movements.md. -
Automatic takeoff / landing suggestions from the lowest foot's trajectory against a perspective-aware ground model (works when the athlete runs towards the camera), with a confidence figure and a list of every other airborne phase in the clip; you confirm or move them.
-
Keypoint correction with interpolation across a frame range, optional Savitzky-Golay or Butterworth smoothing (off by default; corrected points stay put), undo/redo.
-
Scale calibration from the athlete's height or two clicked points, so hip height, loading depth and jump height are also reported in cm.
-
Side-by-side comparison of two open projects with scrubbers aligned on an event (for example takeoff); the second clip's angles are overlaid dashed.
-
Batch mode:
mikiri batch FOLDERtracks and exports every clip in a folder.
<name>/
angles.csv one row per frame: frame, time_s, every angle, hip_y, ankle_L_y,
ankle_R_y, stance_width, per-angle confidence, corrected/lost flags
keypoints.csv all 26 keypoints per frame, pixel coordinates + confidence
events.json name, frame, time_s, source, confidence
keyframes/ PNG overlay at each event frame, plus sheet.png (every Nth frame)
overlay.mp4 skeleton over the video, H.264 yuv420p (optional)
stick.mp4 stick figure only (optional)
summary.md metadata, event table, angles at each event, flight time,
jump height, angle definitions, limitations
project.mikiri.json reopen the analysis in Mikiri
<name>.zip the same, zipped
summary.md reports the flight time between takeoff and landing, the jump
height implied by it, hip rise and loading depth, and explains every column so
the pack stands on its own. Angle conventions are documented in
docs/angles.md.
Click the four court corners you can see (or the near half, or the near
baseline plus the short-service line) once per clip and Mikiri maps the
player's feet onto a top-down court in metres: a court map with the track,
heat map and current position next to the video, footwork metrics (distance,
average and peak speed, direction changes, base position, time per zone,
coverage shape), and positions.csv plus court.png in the export pack. The
camera must be static.
Click the two net-post tops as well. They are verticals of known height (1.55 m) standing at court points Mikiri already knows, so two extra clicks pin down where the camera is. That matters more than it sounds: an ankle keypoint sits about 7.5 cm above the floor, and mapping it as if it were on the floor pushes it away from the camera by up to 92 cm at the far baseline from a 1.6 m tripod. That error is systematic, not noise, so it never averages out. Knowing the camera corrects it, and also yields jump clearance and stroke contact height in metres.
Mikiri propagates its own measurement error rather than asserting an accuracy: the court map draws 95% error ellipses, and the heat map is blurred by that same uncertainty, so the far court is drawn as the smear it really is. Details and accuracy notes in docs/footwork.md; the plan for shuttle tracking is in docs/roadmap.md.
The racket head is estimated one racket length beyond the wrist along the forearm and drawn as a white line, with head speed per frame, contact-point height and swing direction in the smash assessment and racket_* columns in angles.csv. It is an estimate with known limits; read docs/racket.md before quoting a speed.
Once the court is calibrated, a Coaching panel turns the numbers into at most
three things to work on, each with the evidence behind it and how much that
evidence is worth. Pick your level (beginner, club, national, elite) and the
bands tighten; edit coaching.json in the Mikiri folder if you disagree with
them. Cues are phrased for what should happen on the court rather than what a
joint should do, and a data-quality cue fires when the measurement itself is
not good enough to draw a conclusion from. See
docs/coaching.md, and
docs/feature-roadmap.md for the researched plan of
what is worth building next and what is deliberately not.
Mikiri finds each contact and describes it: contact height (overhead, sidearm,
underarm), forehand or backhand, and which part of the court it was played
from. Contacts appear as ticks on the timeline and in a panel that seeks to
them, and as strokes.csv in the export pack.
It stops short of naming the shot from the body alone. Smash, clear and drop share nearly identical overhead kinematics and separate only on racket-face angle and what the shuttle does afterwards, neither of which a single 30 fps camera can see. Rather than guess, Mikiri reports what it can defend and leaves the shot name unset until shuttle evidence supports one. docs/strokes.md explains the reasoning.
Find shuttle in the Strokes panel goes looking for that evidence. Rather than track the shuttle across a whole rally -- which is unreliable on consumer footage -- it searches the few frames after each contact the swing detector already found, starting from where the racket head was. The launch direction and speed it recovers are exactly what separate a smash from a clear from a drop, and contacts where nothing is found simply keep their description.
Shot names also need a side-on camera. The launch is measured in the image, and from behind the baseline a smash is foreshortened until it reads as a drop, so Mikiri checks the recovered camera view and declines to name rather than name it wrong. Film from the side for shot names, from behind and high for footwork. See docs/shuttle.md.
A clip is not a rally, so Mikiri cuts it into bouts of continuous work and reports the rests separately: rally length, distance per rally, metres per minute in play, shots per rally, and -- only when the clip still contains its rests -- work density and effective playing time. That last pair is withheld on footage already trimmed to the rallies, where it would measure the editing rather than the player. docs/rally.md.
Inside the bouts it looks for split steps: of the moves the player made, how many were prepared by a hop, and how long after landing the first step came. It does not attempt split-step timing against the opponent, which needs an opponent this app may not be tracking, and it is honest that what it detects is a preparatory hop of some kind rather than a certified split step. The reasoning, and what the number is worth, is in docs/footwork.md.
With the court calibrated and both players tracked, Mikiri reads who won each rally off who serves the next one, keeps the score, and checks every serve against the rules of the game: contacts alternate, the server stands in the right court on an even score and the left on an odd one, the receiver stands diagonally opposite. Where the rules are broken it asks a question instead of stating a result, and every fact it does state says whether you confirmed it, the rules derived it, or a detector guessed it. A result is never stated on guesses alone. docs/match.md.
From the chain, a Tactics panel gives rally lengths and, per side, the shot mix, the points split by who hit last, and the serve; a percentage is shown only above a minimum count, and every figure says how it was known. Saving the project keeps the session, grouped by the player label in the project options, and the Over time panel reads the open clip against the same player's earlier ones -- the tactical figures and the movement figures the coaching module follows -- withholding a trend that rests on one session. docs/tactics.md.
With two players tracked on a side, a Formation panel gives the shape the pair holds -- side-by-side, front-and-back, or between -- from the line between the partners, the distance between them, and whether the pair took the right shape after its own shots: side-by-side after a lift, front-and-back after a downward shot. It needs no idea of which partner is which, and no shot is attributed to one partner yet. Nothing here has seen real doubles footage; docs/doubles.md says what has.
There are two different set-ups here, and they pull in opposite directions. Side-on is right for joint angles; raised and behind the court is right for footwork. Pick the one that matches the question you are asking.
For joint angles (jumps, lunges, technique):
- Side-on. Put the camera perpendicular to the direction of the jump so the knees, hips and trunk move in the image plane. Filming from behind or at an angle makes angles meaningless and confuses left and right.
- Camera at hip to chest height, on a tripod or leaning on something steady, 4 to 5 m back.
For footwork and court position:
- As high as you can get. Depth error falls off inversely with camera height, and a raised camera also stops the near player hiding the far one. A stand or a balcony beats a tripod by a wide margin.
- As close as framing allows. Depth error grows with the square of distance while height only helps linearly, so higher-and-closer beats further-back. Back off only as far as it takes to fit the court in.
- Get both net posts in frame, base and top, so the camera can be located.
- Expect the far court to be less precise than the near court whatever you do. Mikiri shows you how much less.
- Headroom above the jump and the whole body in frame throughout, including the feet at the deepest load.
- 120 fps or more (240 fps if the phone offers it in good light). Takeoff and landing are sub-frame events at 30 fps.
- Good, even light and a background that contrasts with the athlete. Avoid strong backlight and other people crossing behind.
- Keep the original file. WhatsApp and similar re-encode and drop frames; Mikiri uses the frame index as its primary key and derives time from the measured frame rate, but nothing recovers dropped frames.
- An angle is only correct when the joint's plane of motion is parallel to the camera sensor. Out-of-plane motion (a rotated trunk, a hip that turns in) foreshortens segments and changes the apparent angle; the same jump filmed from a slightly different spot gives different numbers.
- There is no depth from the image alone. Pixel distances become centimetres through the scale calibration, and only in the plane of the athlete. Court positions are different: the floor is a known plane, so those are metres everywhere, with an error that grows with distance from the camera and is reported rather than assumed.
- Shot types (smash, clear, drop) are not classified from pose, because they cannot be. See docs/strokes.md.
- Left and right come from the pose model and can be mirrored when the athlete faces away or turns; use "Swap L/R" and note the camera side.
- Fast feet blur at 30 to 60 fps; ankle and toe keypoints jitter. The auto events use a smoothed signal and hysteresis and report a confidence, but you should confirm them on the frames.
- The pose model was trained on people, not on every racket position; hands and feet behind the body are the first things it loses. Fix them by hand.
- Results are best used comparatively: the same athlete, filmed the same way, over time.
Settings (gear button) cover the pose backend (rtmlib RTMPose by default,
MediaPipe PoseLandmarker as an alternative), the RTMPose size
(lightweight / balanced / performance), local model files if you do not want
any download, the processing downscale (default longest side 1280 px, so long
240 fps clips stay fast and are never loaded into RAM at once), how often the
person detector runs, the confidence threshold below which angles are
blanked, and the ONNX Runtime thread count (0 = auto; on CPUs with many
cores the auto value is markedly faster than ONNX Runtime's own default). Settings and caches live in %LOCALAPPDATA%\Mikiri (or
MIKIRI_HOME).
-
No Python at '"...\python.exe"'when runningpipormikiriinside the venv. The virtual environment's base interpreter moved or is blocked (the shipped.venvwas created byuvfrom a Python living under%APPDATA%\uv, which some corporate policies refuse to execute). Recreate the environment from any normally installed Python 3.11 or newer and reinstall:rmdir /s /q .venv && py -3.11 -m venv .venv && .venv\Scripts\activate.bat && pip install -e .
Replace
py -3.11withC:\Python313\python.exe(or whateverpy -0plists) if 3.11 is not installed. -
OSError: No such file or directoryduringpip installwith a very long path. Windows still limits paths to 260 characters unless long paths are enabled; keep the project in a short folder such asC:\Users\<you>\Desktop\Badminton App. -
First tracking run is slow or fails to download models. The two RTMPose files (about 150 MB) come from download.openmmlab.com with a Hugging Face mirror as fallback; on a machine without internet, download them elsewhere and point Settings at the
.onnxfiles.
mikiri # start the app (default port 8765, opens a browser)
mikiri serve --port 9000 --no-browser --open path/to/clip.mp4
mikiri batch path/to/folder --out path/to/exports --no-videos
mikiri make-demo # regenerate demo/demo.mp4pip install -e .[dev]
python -m playwright install chromium
ruff check src tests
pytest -m "not slow and not ui" # fast unit tests
pytest -m ui # Playwright smoke test (also refreshes the screenshots)
pytest -m slow # real RTMPose pipeline on the demo clip and on the real footage (minutes on CPU)scripts/real_clip_screenshots.py drives a running server with Playwright on
any clip (trim, click the athlete, accept events, screenshot); it produced the
real-footage screenshot above. scripts/render_benchmark.py <clip id> renders
a benchmark clip with the analysis drawn on it -- skeleton, the top-down court,
the bout, the contacts and the split steps -- which is the quickest way to see
whether a number is describing what actually happened.
pytest with no arguments runs all three and is the check to run before
committing; the UI test rewrites the screenshots in docs/screenshots, so
discard that churn unless the interface actually changed.
Layout: src/mikiri (package), src/mikiri/server (FastAPI + static
front end), tests, docs, demo (the synthetic clip), benchmarks (real
footage and what each clip is for). Every non-obvious choice is written up in
DECISIONS.md, and CLAUDE.md is the orientation for
anyone (or anything) picking the codebase up cold.
MIT, see LICENSE. The pose models are downloaded on first run and come under their own terms.







