Skip to content

How it works

Every camera frame goes through the same chain. No piece knows more about the others than it needs to, which is why each one can be tested and replaced on its own.

PoseProvidercamera · recordingPoseMotionSessionVisibility+ SetupCheckRepEngineMeasure + FormRuleMotionSnapshotstate → the screenMotionEventmoments → voice, haptics

A PoseProvider delivers an AsyncStream<Pose>. It can be the camera (VisionPoseProvider), a recording (RecordedPoseProvider) or anything you implement. Each Pose is one frame of body points, in pixels and with sides already corrected.

MotionSession is the front door. For every pose:

  1. Visibility. Are all the joints the exercise measures in view? The change is confirmed after a few frames in a row (~0.25 s) so it doesn’t flicker.
  2. Placement. SetupCheck checks whether the patient faces the camera or stands sideways, is too far away or at the edge of the image.
  3. Engine. RepEngine computes the primary measure, smooths it, checks the form rules and decides whether a rep finished.
  4. Baseline. If one is running, it updates its progress.

Then it publishes the result in two ways.

MotionSnapshot MotionEvent
What it is The whole state, in one value Something that happened on this frame
How to read it session.snapshot (observable) for await event in session.events()
What for Drawing: angle, counter, progress ring Reacting once: say “nice!”, vibrate, save

More detail in Events and snapshot.

The engine has no exercise hard-coded. An ExerciseDefinition describes:

  • what to measure (primary, a Measure),
  • how to count (kind: reps or hold),
  • what form to require (rules, a list of FormRule),
  • where to look from (view: side or front).

Since everything is Codable, a new exercise can arrive as JSON from your server. See Custom exercises.