A Progressive Web App (PWA) built with React, TypeScript, and Material-UI for tracking film photography metadata. This app helps film photographers record and organize their shots with detailed exposure information, location data, and custom notes.
- Configure film parameters (name, ISO, number of exposures)
- Beautiful setup screen with film photography branding
- Live camera view with photo capture capability
- Choose photos from device gallery
- Record exposure settings:
- Aperture (f-stop)
- Shutter speed
- Additional notes
- Automatic location capture (with permission)
- Exposure counter with remaining shots display
- Grid view of all captured exposures
- Quick preview of settings and metadata
- Empty state for new film rolls
- Click to view detailed information
- Import/Export functionality for data backup and sharing
- Full-screen photo view
- Edit all metadata fields
- Change/replace photos
- View capture time and location
- Save changes with validation
- IndexedDB Storage: High-capacity storage (50MB+) for photos and metadata
- Automatic Migration: Seamlessly upgrades from localStorage to IndexedDB
- Quota Management: No more "storage quota exceeded" errors
- Offline-First: All data stored locally on device
- PWA Features: Installation, offline support, and service worker caching
- Storage Resilience: Automatic fallback to localStorage if IndexedDB fails
- Export to Local: Download all photos and metadata as files
- Export to Google Drive: Save directly to Google Drive (requires setup)
- Import from Local: Select exported files from device
- Import from Google Drive: Import from Google Drive folder (requires setup)
- JSON metadata format for easy data portability
- Frontend Framework: React 18 with TypeScript
- Build Tool: Vite
- UI Library: Material-UI (MUI) v5
- PWA: Vite PWA plugin with Workbox
- Storage: Browser LocalStorage API
- Camera: MediaDevices API
- Location: Geolocation API
- Node.js 16+ and npm
- Modern browser with camera support
- HTTPS connection (required for camera access)
- Clone and enter the project directory:
cd film-meta-tracker- Install dependencies:
npm install- Start development server:
npm run dev- Open browser to
http://localhost:5173
npm run buildThe built files will be in the dist directory, ready for deployment.
- Open the app and you'll see the setup screen
- Enter your film details:
- Film Name: e.g., "Kodak Portra 400"
- ISO: Film sensitivity (25-6400)
- Exposures: Number of shots (1-100)
- Click "Start Film Roll"
- In the camera screen, you can:
- Tap camera settings chips to adjust aperture, shutter speed, and add notes
- Use the large camera button to capture or start camera
- Use "Gallery" button to select existing photos
- View exposure counter in top header
- Each photo automatically captures:
- Current date/time
- Location (if permission granted)
- Your manual settings
- Gallery View: Tap the gallery icon to see all exposures
- Details View: Tap any exposure to view/edit details
- Editing: Use the edit button to modify settings and notes
- Photo Changes: Replace photos using camera or gallery options
- In the gallery view, tap the Export button
- Enter a folder name for your export
- Choose export method:
- Local Download: Downloads all files to your device
- Google Drive: Saves to Google Drive (requires API setup)
- Click Export to start the process
- In the gallery view, tap the Import button
- Choose import method:
- Local Files: Select the exported files from your device
- Google Drive: Enter the folder name in Google Drive
- Click Import to restore your data
Export Format:
metadata.json: Contains all film roll and exposure informationexposure_X_ID.jpg: Individual photos with numbered naming- All files organized in a single folder for easy management
src/
βββ components/ # React components
β βββ SetupScreen.tsx # Film roll configuration
β βββ CameraScreen.tsx # Photo capture interface
β βββ GalleryScreen.tsx # Exposure list view
β βββ DetailsScreen.tsx # Individual exposure details
βββ utils/ # Utility functions
β βββ storage.ts # LocalStorage management
β βββ camera.ts # Camera and file utilities
βββ types.ts # TypeScript type definitions
βββ App.tsx # Main application component
- Camera Access: Chrome 53+, Firefox 36+, Safari 11+
- PWA Features: Chrome 40+, Firefox 44+, Safari 11.1+
- Local Storage: All modern browsers
- Geolocation: All modern browsers
- Local Storage Only: All data stays on your device
- No Server Communication: App works completely offline
- Permission-Based: Camera and location access require user consent
- No Tracking: No analytics or data collection
The app supports Google Drive integration for cloud backup, but requires additional setup:
-
Create a Google Cloud Project:
- Visit Google Cloud Console
- Create a new project or select existing one
-
Enable Google Drive API:
- Navigate to "APIs & Services" > "Library"
- Search for "Google Drive API" and enable it
-
Create Credentials:
- Go to "APIs & Services" > "Credentials"
- Click "Create Credentials" > "API Key"
- Restrict the API key to Google Drive API
-
Add Google APIs JavaScript Client:
<!-- Add to index.html --> <script src="https://apis.google.com/js/api.js"></script>
-
Initialize in your app:
- Update the
googleDriveUtils.isAvailable()function - Implement authentication flow
- Handle file upload/download operations
- Update the
Note: Without Google Drive setup, all import/export operations will use local file downloads, which works perfectly for most use cases.
npm run dev- Start development servernpm run build- Build for productionnpm run preview- Preview production buildnpm run lint- Run ESLint
- Fork the repository
- Create a feature branch:
git checkout -b feature-name - Commit changes:
git commit -am 'Add feature' - Push to branch:
git push origin feature-name - Submit a pull request
npm run build
npx vercel --prodnpm run build
npx netlify deploy --prod --dir=distnpm run build
# Deploy dist/ directory to gh-pages branchMIT License - see LICENSE file for details
- Material-UI for the component library
- Vite for the fast build tool
- Workbox for PWA capabilities
- The film photography community for inspiration