Skip to content

Latest commit

Β 

History

13 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Gestify

Add gesture navigation to your website with ease.

Version License Size Types


Gestify lets visitors navigate your website using hand gestures through their webcam. It's privacy-first, needs no API key, and takes one line of code to integrate.

Gestify.init();

The SDK handles everything β€” consent prompt, camera permission, gesture detection β€” automatically.


Why Gestify

Most gesture libraries are complex. Gestify is not.

  • No configuration required β€” works out of the box
  • No API key, no server β€” fully offline capable
  • No video leaves the browser β€” all processing is local
  • No buttons to wire up β€” built-in consent modal
  • No framework lock-in β€” works everywhere

Installation

npm install gestify

CDN (no build step required):

<script src="https://unpkg.com/gestify/dist/gestify.min.js"></script>

Quick Start

npm / ESM

import Gestify from 'gestify';

Gestify.init();

CDN / Script tag

<script src="https://unpkg.com/gestify/dist/gestify.min.js"></script>
<script>
  Gestify.init();
</script>

That's it. Gestify shows a consent prompt on first visit, remembers the user's choice, and handles everything automatically from there.


Profiles

Choose a preset tuned for your use case:

Gestify.init({ profile: 'presentation' });
Profile Best For Speed Hold Time
default Most websites 1Γ— 700 ms
presentation Slides, demos 1.3Γ— 650 ms
accessibility Motor impairments 0.8Γ— 1000 ms
fast Power users 1.7Γ— 500 ms
gaming Interactive UIs 2Γ— 400 ms

Configuration

Fine-tune individual parameters on top of any profile:

Gestify.init({
  profile: 'default',
  overrides: {
    holdTime: 800,           // ms to hold open-palm to enter cursor mode
    exitHoldTime: 2000,      // ms to hold fist to exit cursor mode
    gestureSpeed: 1.2,       // cursor speed multiplier
    scrollSpeed: 1.0,        // scroll speed multiplier
    clickCooldown: 700,      // ms between gesture-triggered clicks
    cursorModeEnabled: true, // enable cursor-mode gestures
    sensitivity: 'medium',   // 'low' | 'medium' | 'high'
  },
  selector: 'button, a',     // custom CSS selector for gesture targets
  debug: true,               // enable [Gestify] console logs
});

API Reference

Gestify.init(options?) β€” start here

Initializes Gestify and runs the full consent flow automatically.

Scenario What Gestify does
First visit Shows a consent toast
Previously enabled Silently restores camera
Camera already granted Starts immediately
Camera blocked Stays quiet
Dismissed this session Stays quiet

Gestify.enable() β€” advanced

Programmatically start gestures, bypassing the built-in modal. Only needed if you're building a custom consent UI.

Gestify.disable()

Stop gesture tracking. Camera is released. SDK stays initialized.

Gestify.destroy()

Full teardown β€” stops camera, removes all DOM elements, clears observers.

Gestify.isEnabled() β†’ boolean

Gestify.getPermissionState() β†’ Promise<'granted' | 'denied' | 'prompt' | 'unknown'>

Cursor Mode

Gestify.enableCursorMode()
Gestify.disableCursorMode()
Gestify.toggleCursorMode()
Gestify.isCursorModeActive() // β†’ boolean

Framework Examples

React

// components/GestureInit.jsx
import { useEffect } from 'react';

export default function GestureInit({ profile = 'default' }) {
  useEffect(() => {
    import('gestify').then(({ default: Gestify }) => Gestify.init({ profile }));
    return () => import('gestify').then(({ default: Gestify }) => Gestify.destroy());
  }, []);
  return null;
}

// App.jsx
import GestureInit from './GestureInit';

export default function App() {
  return <>
    <GestureInit />
    {/* rest of your app */}
  </>;
}

Next.js

// components/GestureInit.jsx
'use client';
import { useEffect } from 'react';

export default function GestureInit({ profile = 'default' }) {
  useEffect(() => {
    import('gestify').then(({ default: Gestify }) => Gestify.init({ profile }));
    return () => import('gestify').then(({ default: Gestify }) => Gestify.destroy());
  }, []);
  return null;
}

App Router (app/layout.jsx):

import GestureInit from '@/components/GestureInit';

export default function RootLayout({ children }) {
  return <html><body>{children}<GestureInit /></body></html>;
}

Pages Router (_app.jsx):

import dynamic from 'next/dynamic';
const GestureInit = dynamic(() => import('../components/GestureInit'), { ssr: false });

export default function App({ Component, pageProps }) {
  return <><Component {...pageProps} /><GestureInit /></>;
}

Vue 3

<!-- components/GestureInit.vue -->
<template><!-- Gestify renders its own UI --></template>

<script setup>
import { onMounted, onUnmounted } from 'vue';
const props = defineProps({ profile: { type: String, default: 'default' } });

onMounted(async () => {
  const { default: Gestify } = await import('gestify');
  Gestify.init({ profile: props.profile });
});

onUnmounted(async () => {
  const { default: Gestify } = await import('gestify');
  Gestify.destroy();
});
</script>

App.vue:

<template>
  <GestureInit />
  <RouterView />
</template>

<script setup>
import GestureInit from '@/components/GestureInit.vue';
</script>

Angular

// gesture-init.component.ts
import { Component, OnDestroy, Input } from '@angular/core';

@Component({ selector: 'app-gesture-init', standalone: true, template: '' })
export class GestureInitComponent implements OnDestroy {
  @Input() profile = 'default';
  private sdk: any = null;

  constructor() {
    if (typeof window === 'undefined') return; // SSR guard
    import('gestify').then(({ default: Gestify }) => {
      this.sdk = Gestify;
      Gestify.init({ profile: this.profile });
    });
  }

  ngOnDestroy() { this.sdk?.destroy(); }
}

TypeScript

import Gestify from 'gestify';
import type { GestifyInitOptions } from 'gestify';

const options: GestifyInitOptions = {
  profile: 'accessibility',
  overrides: { holdTime: 1200, sensitivity: 'high' },
  debug: true,
};

Gestify.init(options);

window.addEventListener('gestify:enabled', () => {
  console.log('Gesture navigation active');
});

window.addEventListener('gestify:click', (e) => {
  const { x, y } = e.detail; // fully typed
});

Events

Listen to Gestify events on window:

window.addEventListener('gestify:enabled',          () => { /* started */ });
window.addEventListener('gestify:disabled',         () => { /* stopped */ });
window.addEventListener('gestify:cursorModeEnter',  () => { /* cursor on */ });
window.addEventListener('gestify:cursorModeExit',   (e) => console.log(e.detail.reason));
window.addEventListener('gestify:click',            (e) => console.log(e.detail)); // { x, y }
window.addEventListener('gestify:scroll',           (e) => console.log(e.detail)); // { direction, amount }
window.addEventListener('gestify:permissionDenied', (e) => console.log(e.detail.reason));
window.addEventListener('gestify:consentDismissed', () => { /* user clicked No */ });
window.addEventListener('gestify:error',            (e) => console.error(e.detail.message));

Browser Support

Browser Minimum Version
Chrome 90+
Edge 90+
Firefox 90+
Safari 15+

HTTPS required. Camera access is restricted to secure origins. localhost works in development.

Desktop only. Mobile and tablet devices are not supported β€” gesture control requires a desktop webcam.


Privacy

Gestify is built privacy-first:

πŸ”’ All processing is local Powered by MediaPipe β€” runs entirely in the browser
🚫 No video transmitted Camera frames never leave the device
🚫 No analytics Zero data collection
🚫 No background access Camera only activates after explicit user consent
βœ… User is in control Can disable at any time; consent persists across sessions

Gestify is compliant with GDPR, CCPA, and similar privacy regulations.


Troubleshooting

Camera permission denied

const state = await Gestify.getPermissionState();
// If 'denied' β€” ask user to reset via browser Settings β†’ Privacy β†’ Camera

No gestures detected

  • Ensure good lighting (avoid strong backlight)
  • Keep hand 30–60 cm from camera
  • Try profile: 'accessibility' for more forgiving thresholds
  • Enable debug: true and check the browser console

SSR errors (Next.js, Nuxt, Angular Universal)

Always use a dynamic import β€” Gestify is browser-only:

// βœ… correct
import('gestify').then(({ default: Gestify }) => Gestify.init());

// ❌ incorrect β€” will break SSR
import Gestify from 'gestify';

Content Security Policy (CSP)

Gestify loads MediaPipe from jsDelivr. Allow it:

Content-Security-Policy: script-src 'self' https://cdn.jsdelivr.net;

Contributing

Pull requests are welcome. See CONTRIBUTING.md to get started.


Changelog

See CHANGELOG.md.


License

MIT Β© Gestify Contributors

About

Add hand gesture navigation to your website with ease.

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages