DEV Community

Beck_Moulton
Beck_Moulton

Posted on

Stop Slouching! Build a Real-time AI Posture Guard with MediaPipe and Vue.js

Let's be honest: as engineers, our "focus mode" usually involves leaning into the monitor until our noses almost touch the screen. We start the day sitting like a king and end it looking like a question mark. Back pain isn't just a meme; it's a productivity killer.

In this tutorial, we’re building Posture Guardian, a browser-based tool using real-time computer vision and pose estimation to detect slouching, forward head posture (the "tech neck"), and uneven shoulders. We'll leverage MediaPipe and Vue.js to create a seamless experience that alerts you the moment your ergonomics fail. If you’ve been looking for a practical application for machine learning in the browser, this is it.

The Architecture 🏗️

How does a browser "know" you're slouching? We need to capture frames from the webcam, process them through a pre-trained model to find skeletal landmarks, and then apply some basic trigonometry to calculate angles.

graph TD
    A[Webcam Stream] --> B[MediaPipe Pose Model]
    B --> C{Landmark Detection}
    C -->|Coordinates| D[Ergonomics Logic Engine]
    D --> E[Calculate Neck & Shoulder Angles]
    E --> F{Threshold Exceeded?}
    F -->|Yes| G[Browser Notification + Visual Alert]
    F -->|No| H[Status: Healthy]
    G --> A
    H --> A
Enter fullscreen mode Exit fullscreen mode

Prerequisites 🛠️

To follow along, you’ll need:

  • Vue.js 3 (Composition API)
  • MediaPipe Pose (@mediapipe/pose)
  • A basic understanding of Javascript/TypeScript
  • A webcam (obviously!)

Step 1: Setting Up the Pose Engine

First, we need to initialize the MediaPipe Pose model. This model identifies 33 landmarks on the human body. For posture, we specifically care about the ears, shoulders, and hips.

// postureService.js
import { Pose } from "@mediapipe/pose";

export const createPoseEstimator = (onResults) => {
  const pose = new Pose({
    locateFile: (file) => `https://cdn.jsdelivr.net/npm/@mediapipe/pose/${file}`,
  });

  pose.setOptions({
    modelComplexity: 1,
    smoothLandmarks: true,
    minDetectionConfidence: 0.5,
    minTrackingConfidence: 0.5,
  });

  pose.onResults(onResults);
  return pose;
};
Enter fullscreen mode Exit fullscreen mode

Step 2: The Math of "Slouching" 📐

To detect "Head Forward" posture, we calculate the horizontal distance and angle between the Ear and the Shoulder. If the ear moves too far forward relative to the shoulder line, you're officially a turtle. 🐢

// Logic to detect Forward Head Posture
function analyzePosture(landmarks) {
  const leftEar = landmarks[7];
  const rightEar = landmarks[8];
  const leftShoulder = landmarks[11];
  const rightShoulder = landmarks[12];

  // Calculate the average ear-to-shoulder horizontal offset
  const earMidX = (leftEar.x + rightEar.x) / 2;
  const shoulderMidX = (leftShoulder.x + rightShoulder.x) / 2;

  const diff = Math.abs(earMidX - shoulderMidX);

  // If the head is more than 15% forward relative to the body
  if (diff > 0.15) {
    return { status: 'bad', message: 'Sit up straight! Your neck is straining.' };
  }
  return { status: 'good', message: 'Perfect Posture!' };
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Integrating with Vue.js

Now, let's wrap this in a Vue component. We'll use the Webcam API to feed the video stream into the Pose model.

<template>
  <div class="posture-container">
    <video ref="videoElement" class="hidden-video" autoplay></video>
    <canvas ref="canvasElement" class="overlay-canvas"></canvas>

    <div :class="['alert-box', statusClass]">
      <h2>{{ currentStatus }}</h2>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue';
import { createPoseEstimator } from './postureService';

const videoElement = ref(null);
const canvasElement = ref(null);
const currentStatus = ref('Initializing...');
const statusClass = ref('neutral');

onMounted(async () => {
  const pose = createPoseEstimator((results) => {
    // Draw landmarks on canvas
    drawUserFeedback(results);

    // Run our logic
    const analysis = analyzePosture(results.poseLandmarks);
    currentStatus.value = analysis.message;
    statusClass.value = analysis.status;

    if (analysis.status === 'bad') {
       triggerNotification();
    }
  });

  const camera = new Camera(videoElement.value, {
    onFrame: async () => {
      await pose.send({ image: videoElement.value });
    },
    width: 640,
    height: 480
  });
  camera.start();
});
</script>
Enter fullscreen mode Exit fullscreen mode

Advanced Patterns & Production Readiness 🚀

Building a simple detection tool is one thing, but making it production-ready involves handling lighting variations, camera calibration, and state management (to avoid spamming notifications every second).

For deeper dives into advanced AI patterns, production-grade Mediapipe configurations, or how to optimize TensorFlow.js performance in large-scale Vue apps, I highly recommend checking out the technical deep-dives over at WellAlly Blog. They cover excellent strategies for integrating wellness tech into the modern developer workflow.

Step 4: Browser Notifications 🔔

Don't just show a message on the screen—tell the user even if they are in another tab.

function triggerNotification() {
  if (Notification.permission === "granted") {
    // Throttle notifications so we don't annoy the user
    if (Date.now() - lastNotificationTime > 60000) {
      new Notification("Posture Alert! 🚨", {
        body: "You're slouching again. Take a deep breath and sit up.",
        icon: "/guardian-logo.png"
      });
      lastNotificationTime = Date.now();
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

By combining the power of MediaPipe with a reactive framework like Vue.js, we’ve built a tool that actually improves your daily life. No expensive hardware, just a few lines of JavaScript and a webcam.

What's next?

  1. Add a "Stretch Timer" that triggers every 25 minutes.
  2. Use LocalStorage to track your "Posture Score" throughout the day.
  3. Implement "Eyes Strain" detection by monitoring blink rates.

How do you stay ergonomic at your desk? Let me know in the comments below! And don't forget to fix your posture right now—I know you're leaning forward while reading this. 😉

Happy coding! 🥑

Top comments (0)