Hand Frame Portal is a browser-based camera experience built with Vite and TypeScript. It uses MediaPipe Hand Landmarker and the Canvas 2D API to detect hands in real time, recognize a hand framing gesture, freeze the scene, and let the user capture, preview, and download an image.
The project is intentionally small and focused: a single-page web app with no backend, no framework runtime, and a UI layered directly on top of a fullscreen canvas.
- Real-time hand tracking in the browser
- Visual hand landmark and connection rendering on a fullscreen canvas
- Gesture-based frame freeze flow
- Hold-to-freeze interaction for more stable detection
- Configurable capture delay: instant, 3 seconds, 5 seconds, or 10 seconds
- Preview modal before saving the captured image
- PNG download flow for captured output
- Reset action to restart the experience quickly
- Mobile-friendly fullscreen layout with responsive controls
- Vite
- TypeScript
@mediapipe/tasks-vision- Canvas 2D
- Browser camera APIs via
navigator.mediaDevices.getUserMedia
At a high level, the app does the following:
- Requests camera access from the browser.
- Loads the MediaPipe Hand Landmarker runtime and hand model.
- Reads frames from the live video stream.
- Detects hand landmarks in real time.
- Builds and stabilizes a polygon from the detected hand pose.
- Freezes the current scene when the user holds the framing gesture steadily.
- Lets the user start a delayed capture, preview the result, and download it.
The UI is rendered as a combination of:
- a fullscreen canvas for live drawing and image composition
- lightweight DOM controls for status, capture options, reset, and preview
Before running the project, make sure you have:
- Node.js 18 or newer recommended
- npm available in your environment
- A modern browser with camera support
- Permission to access a webcam
- Internet access to load MediaPipe assets from remote URLs
Install dependencies:
npm installStart the development server:
npm run devVite will print a local development URL in the terminal. Open that URL in your browser and allow camera access when prompted.
Create a production build:
npm run buildPreview the built app locally:
npm run previewThis project is a static frontend application, so the recommended production setup is a Docker multi-stage build that compiles the app with Node.js and serves the final dist/ output through Nginx.
Auto deploy smoke test: this line is intentionally safe to change when validating Dokploy watch path triggers.
Recommended target:
- Platform: Dokploy
- Source: GitHub repository
- Build type:
Dockerfile - Runtime server: Nginx
- Domain:
sayhi.ngxtm.site
This repository includes the following deployment files:
Dockerfile.dockerignorenginx.conf
- Create a new application in Dokploy.
- Connect the application to your GitHub repository.
- Select the branch you want to deploy.
- Choose
Dockerfileas the build method. - Use the repository root as the build context.
- Set the Dockerfile path to
./Dockerfile. - Set the internal application port to
80. - Attach the domain
sayhi.ngxtm.site. - Enable SSL / Let's Encrypt.
- Run the first deployment manually, then enable auto deploy if the result is stable.
Create an A record for sayhi.ngxtm.site that points to your VPS public IP.
If you use Cloudflare, it is usually safer to start in DNS-only mode for the initial verification, then enable proxying later if needed.
- Camera access requires
HTTPSin production. If the site is served over plain HTTP, most browsers will block webcam access. - The app loads MediaPipe runtime and model assets from external URLs.
- Client browsers must be able to access:
https://cdn.jsdelivr.nethttps://storage.googleapis.com
After deployment, verify the following on https://sayhi.ngxtm.site:
- The page loads without missing CSS or JavaScript files.
- The browser prompts for camera access.
- MediaPipe initializes successfully.
- Hand detection works in real time.
- Freeze, delayed capture, preview, and download all work as expected.
- The browser console does not show mixed-content, asset loading, or camera permission errors.
Defined in package.json:
npm run dev: starts the Vite development servernpm run build: runs TypeScript checks and creates a production build with Vitenpm run preview: serves the production build locally for verification
- Open the app in a supported browser.
- Allow camera access.
- Move your hand into view.
- Open your palm and hold the framing gesture steady for about one second.
- Wait for the app to freeze the scene.
- Choose a capture delay from the selector.
- Click the capture button.
- Review the preview image.
- Download the image if it looks correct.
- Use
Resetto clear the frozen state and start over.
.
├─ index.html
├─ package.json
├─ package-lock.json
├─ tsconfig.json
├─ Dockerfile
├─ nginx.conf
├─ src/
│ ├─ main.ts
│ └─ style.css
└─ README.md
- The application entry point is
src/main.ts. - Styling lives in
src/style.css. - The UI copy is currently written in Vietnamese, while this README is in English.
- The app uses remote MediaPipe assets rather than bundling model files into the repository.
- The capture flow is client-side only; there is no server upload or persistence layer.
- Camera permission is required. If access is denied, the app cannot initialize correctly.
- Hand tracking quality depends on lighting, camera resolution, background clutter, and device performance.
- Network restrictions can block MediaPipe runtime or model asset loading.
- Browser support may vary, especially on older mobile devices or browsers with limited media capabilities.
- The current repository does not include automated tests.
If the app does not start correctly:
- Confirm that your browser has permission to use the camera.
- Check that the webcam is not already locked by another application.
- Verify that your internet connection allows access to external MediaPipe asset URLs.
- Refresh the page after granting permissions.
- Re-run
npm installif dependencies are missing.
If gesture tracking feels unstable:
- Use brighter and more even lighting.
- Keep your hand clearly visible and centered in frame.
- Reduce background clutter behind the hand.
- Try a device with a better camera or more GPU/CPU headroom.
This repository includes a LICENSE file. See that file for the applicable license terms.