A Progressive Web App (PWA) that automatically detects faces and crops photos to head-and-shoulders portraits. Works offline after initial load.
Deployed at https://fabboe.github.io/offline-portrait-cropper/
- Automatic Face Detection: Uses TensorFlow.js BlazeFace model for accurate face detection
- Smart Cropping: Automatically crops to include head and shoulders with a fixed 3:4 aspect ratio
- Manual Fine-Tuning: Adjust the automatic crop with Cropper.js drag-and-drop interface
- Offline Support: Full PWA with service worker - works without internet after first load
- Drag & Drop: Easy-to-use interface with drag-and-drop file upload
- High Quality Output: Exports 800×1000px portraits optimized for profile photos
- Privacy First: All processing happens locally in your browser - no server uploads
- Framework: Vanilla JavaScript (ES modules)
- Build Tool: Vite
- Face Detection: TensorFlow.js + BlazeFace model
- Image Processing: HTML5 Canvas API
- Manual Cropping: Cropper.js
- PWA: Vite PWA Plugin with Workbox
- Node.js 18+ and npm
- Clone or navigate to this repository:
cd offline-portrait-cropper- Install dependencies:
npm install- Start development server:
npm run dev- Open your browser to the URL shown (typically
http://localhost:5173)
Build the production-ready PWA:
npm run buildPreview the production build:
npm run previewThe built files will be in the dist/ directory, ready to deploy to any static hosting service.
- Upload: Drag & drop a photo or click to upload
- Detection: BlazeFace model detects face location
- Auto-Crop: Automatically expands around face to include shoulders
- Aspect Ratio: Enforces 4:5 ratio (perfect for portraits)
- Fine-Tune (optional): Click "Fine-Tune Crop" to manually adjust the crop area
- Output: Renders 800×1000px high-quality portrait
- Download: Save as JPEG with one click
Deploy to any static hosting service:
npm install -g vercel
vercelnpm install -g netlify-cli
netlify deploy --prod --dir=distThis project includes automatic GitHub Pages deployment!
See DEPLOY.md for detailed instructions.
Quick setup:
- Push your code to GitHub
- Enable GitHub Pages in Settings → Pages → Source: GitHub Actions
- Your site will automatically deploy on every push!
Your app will be live at: https://YOUR-USERNAME.github.io/offline-portrait-cropper/
offline-portrait-cropper/
├── index.html # Main HTML with UI
├── app.js # Core application logic
├── style.css # Custom Cropper.js styles
├── vite.config.js # Vite + PWA configuration
├── package.json # Dependencies
├── public/ # Static assets
│ ├── icon.png # PWA icons
│ ├── icon-192.png
│ └── icon-512.png
└── dist/ # Production build output
Edit the expansion multipliers in app.js:107-110:
const horizontalExpand = 0.4; // 40% on each side (wider/narrower)
const topExpand = 0.6; // 60% above face (more/less headroom)
const bottomExpand = 1.2; // 120% below face (more/less shoulders)Modify the target aspect ratio in app.js:120:
const targetAspect = 4 / 5; // Current: 4:5 (portrait)
// Examples:
// 1 / 1 -> Square (1:1)
// 2 / 3 -> Classic portrait
// 3 / 4 -> Standard photoAdjust output dimensions in app.js:141-142:
const outputWidth = 800; // Width in pixels
const outputHeight = 1000; // Height in pixels- Chrome/Edge 90+
- Firefox 88+
- Safari 14+
- Opera 76+
All modern browsers with Canvas API and ES6 module support.
After the first visit:
- App shell cached (HTML, CSS, JS)
- TensorFlow.js models cached
- Works without internet connection
- Install as app on mobile/desktop
- No data collection: Zero analytics or tracking
- Local processing: All images processed in your browser
- No uploads: Images never leave your device
- No storage: Images not saved (except when you download)
- First load: ~5-10s (downloads TensorFlow models ~8MB)
- Subsequent loads: <1s (cached)
- Processing time: 1-3s per image (depends on image size)
- Model preloads on page load for faster first use
"No face detected" error:
- Ensure face is clearly visible and well-lit
- Face should be front-facing
- Try a different photo
Slow performance:
- Use smaller images (<5MB)
- Clear browser cache and reload
- Check if WebGL is enabled in browser
PWA not installing:
- Must be served over HTTPS (or localhost)
- Build production version:
npm run build - Check browser DevTools > Application > Manifest
- Arrow keys: Move crop box by 1 pixel
- Shift + Arrow keys: Move crop box by 10 pixels
MIT License - Feel free to use for any purpose
- Face Detection: TensorFlow.js BlazeFace
- Manual Cropping: Cropper.js
- Build Tool: Vite
- PWA Plugin: vite-plugin-pwa