PoseTracker is a real-time pose estimation SDK and API for fitness and rehab apps. It runs MoveNet on-device on React Native, iOS, Android and the web. Keypoints are free. An optional API key adds rep counting, joint angles and a form score.
Guide: https://www.posetracker.com/news/react-native-pose-estimation-expo
Docs: https://docs.posetracker.com/quickstart
⭐ Star us on GitHub: Movelytics/react-native-pose-estimation — it helps other React Native and Expo developers find the SDK.
Try it on your phone (no Xcode / Android Studio):
- Install Expo Go
- Open the Expo Snack → Run on device
- Scan the QR — live pose estimation, no API key
Steps and QR: https://docs.posetracker.com/try-expo-go
PoseTracker is a production-ready human pose estimation SDK for React Native, fully optimized for iOS and Android (including Expo Go). It runs MoveNet SinglePose Lightning on-device in a WebView (TF.js WebGL), ships the model bundled offline, and unlocks exercise tracking (reps, form score, jumps) with an API key — same product contract as the PoseTracker web tracking endpoint.
| Capability | Detail |
|---|---|
| Platforms | iOS + Android, bare RN and Expo / Expo Go |
| Model | MoveNet SinglePose Lightning (17 COCO keypoints), bundled — no model download. Opt-in model: 'blazepose' loads BlazePose from CDN (unused MoveNet still in the package — prefer light). |
| Free tier | Pose estimation + keypoints without an API key, offline |
| Paid tier | Remote movement engine: squat / push-up / jumps, counter.form_score, angles… |
| UX | Branded loading screen, PoseTracker skeleton overlay, plan-gated watermark |
| Peers | react-native-webview (required); optional FS helpers for engine cache |
This package is the offline / bundled SDK (9.9 MB packed). If you need a
much smaller install and can require network for TF.js + MoveNet each session,
use the sibling Light SDK (206 kB packed):
- npm:
@pose-tracker/react-native-pose-estimation-light - GitHub: https://github.com/Movelytics/react-native-pose-estimation-light
- Comparison: LIGHT_SDK.md
Same free keypoints + paid engine API surface; light fetches the model at boot.
Agents: shared UX/API/bugfixes → mirror to light (or ask first). See
DUAL_SDK_CHANGES.md.
npm install @pose-tracker/react-native-pose-estimation react-native-webview
# Expo:
npx expo install react-native-webview expo-cameranpm:
@pose-tracker/@pose-tracker/react-native-pose-estimation
GitHub: https://github.com/Movelytics/react-native-pose-estimation
Required: host app must declare camera permissions — see PERMISSIONS.md.
Media inputs (v0.2): camera (default), uploaded video, still image — host picks the file. See MEDIA_SOURCES.md and https://docs.posetracker.com/media-sources.
import {
PoseTrackerProvider,
WebViewPoseView,
usePoseTracker,
} from '@pose-tracker/react-native-pose-estimation';
function App() {
return (
<PoseTrackerProvider>
<CameraScreen />
</PoseTrackerProvider>
);
}
function CameraScreen() {
usePoseTracker({
onKeypoints: (e) => {
// 17 keypoints every frame — works offline, no API key
console.log(e.keypoints.length, e.score);
},
});
return (
<WebViewPoseView
style={{ flex: 1 }}
drawSkeleton
loadingText="AI Loading"
// Default source="camera". Host-picked file:
// source="image" sourceUri={fileUri}
// source="video" sourceUri={fileUri}
// or sourceBase64 + sourceMime
/>
);
}Conceptual equivalent of:
https://app.posetracker.com/pose_tracker/tracking?token=YOUR_API_KEY&exercise=squat&skeleton=true
<PoseTrackerProvider
apiToken="YOUR_API_KEY"
options={{ features: { angles: true, progression: true, minGrade: 'B' } }}
>
<WebViewPoseView drawSkeleton skeletonUuid="OPTIONAL_CUSTOM_SKELETON_UUID" />
</PoseTrackerProvider>const { preload, startExercise } = usePoseTracker({
onMessage: (msg) => {
if (msg.type === 'counter') {
// current_count + form_score: { score, avg_score, grade }
}
},
});
await preload(); // basic cold-start: model only, no camera permission
startExercise('squat');Full guide, including Vision Camera and expo-camera: https://docs.posetracker.com/external-frames
Optional. Use it when your app already owns the camera (VisionCamera, a custom pipeline, a recorded file) and you only need the data. PoseTracker runs pose estimation and the exercise engine on every frame you push, and returns the same events as the camera flow: keypoints, posture/placement, counter, form score, progression. Nothing is drawn, so you render what you want.
const { warmupExternal, startExercise, processFrame } = usePoseTracker({
onCounter: (e) => setReps(e.count), // listeners still fire
});
await warmupExternal(); // no camera permission, no getUserMedia
startExercise('squat');
// For each frame from your camera:
const { dropped, pose, events } = await processFrame({
base64: jpegBase64, // or uri: 'file:///…' / 'data:image/jpeg;base64,…'
width: 256,
height: 192,
timestampMs: Date.now(),
mirrored: true, // front camera (default). false for the back camera.
});
if (!dropped) {
// pose.keypoints are normalized (0..1) to the frame you sent
// events: everything emitted for this frame (counter, posture, …)
}- The Provider mounts a hidden 1×1 warmer after
warmupExternal(). It is not needed if a<WebViewPoseView />is already on screen. - One frame in flight at a time: an overlapping call resolves at once with
{ dropped: true, pose: lastPose, events: [] }. Await before pushing the next one. - Keep the longest side of each frame at about 256 px or less. A 1080p JPEG per frame is not real-time over the bridge.
- The engine is temporal: push frames in order and keep the same session across frames.
- Don't mix the two flows.
processFramethrows while the SDK camera is open. processFramebeforewarmupExternal()throws.
| Mode | API | Camera permission |
|---|---|---|
| basic (default) | preload() |
No — model / WebGL only |
| full | preload({ coldStart: 'full' }) |
Yes — only when user expects camera |
Lobby warmer: <WebViewPoseView coldStart="basic" />.
Camera screen: <WebViewPoseView /> (coldStart="full").
| Doc | Topic |
|---|---|
| PERMISSIONS.md | Camera permission setup (required) |
| PRELOAD.md | Preload / warm-up / lifecycle |
| FEATURES.md | Plan gating, watermark, loading text |
| EVENTS.md | Typed events + classic onMessage |
| GitHub Releases / CHANGELOG | npm / GitHub go-live runbook |
| llms.txt | Machine-readable product facts (GEO) |
Does it work without an API key?
Yes. Keypoints-only mode is free and offline.
Is it optimized for both iOS and Android?
Yes. Same WebView MoveNet Lightning path on both; adaptive capture quality.
Does it support Expo Go?
Yes (WebView peer). Apple Vision backend is optional and not available in Expo Go.
BlazePose / MediaPipe?
Pass model: 'blazepose' on PoseTrackerProvider options. Loads
@tensorflow-models/pose-detection from jsDelivr in the WebView (lite /
TF.js, COCO-17). Requires network. This offline package still ships
bundled MoveNet which is unused in that mode and inflates the app — Metro
logs a warning. Prefer
@pose-tracker/react-native-pose-estimation-light
if you do not need offline MoveNet. Native MediaPipe is not in this SDK.
Who sees the “Powered by PoseTracker” watermark?
Keyless and free plans. It is a link to posetracker.com (utm_medium=react-native), opened with Linking.openURL. Hidden for paid plans (developer / company / enterprise…). Set showWatermark={false} to remove it.
How does Claude or Cursor integrate this SDK?
Add the connector https://mcp.posetracker.com/api/mcp, sign in, and click Allow at https://app.posetracker.com/oauth/authorize. The agent writes the snippet on your account. Docs: https://docs.posetracker.com/ai/mcp-connector.
Proprietary — Movelytics SAS / PoseTracker. See LICENSE.
Third-party components (TensorFlow.js, MoveNet Lightning) are Apache 2.0 —
see THIRD_PARTY_NOTICES.md.
- Product: https://www.posetracker.com
- Docs: https://docs.posetracker.com
- Try on your phone: https://docs.posetracker.com/try-expo-go
- Expo Snack: https://snack.expo.dev/@fsepret/posetracker-sdk-demo-app
- Demo app: https://github.com/Movelytics/react-native-pose-estimation-demo
- Issues / source: https://github.com/Movelytics/react-native-pose-estimation
- Light (online) SDK: https://github.com/Movelytics/react-native-pose-estimation-light