Inspiration Every day, photographers, artists, and content creators upload their work to the internet — and lose control of it almost instantly. We've seen friends have their artwork stolen, screenshots reposted without credit, and original photos used commercially without permission. Existing tools either slap an ugly visible watermark on top of the image or rely entirely on platform-level DRM that disappears the moment someone takes a screenshot.
We wanted to build something different: a tool that protects images before they leave your hands, using the same math that powers JPEG compression to hide ownership data inside the image itself, invisible to the human eye, but detectable by anyone who knows where to look. What It Does
PhotoGuard is a full-stack photo protection platform that gives creators three layers of defense: Invisible browser-side watermark. Before an image is ever uploaded, we embed a near-invisible ownership tag (1% opacity canvas overlay) directly in the browser. This survives screenshots and screen grabs.
DCT steganographic watermark. Our backend embeds ownership data into the mid-frequency coefficients of the image's DCT (Discrete Cosine Transform), the same mathematical domain used by JPEG compression. This watermark is imperceptible to the naked eye and survives JPEG re-encoding.
Cryptographic proof-of-creation. Before upload, we compute a SHA-256 fingerprint of the file using the browser's Web Crypto API. This creates a timestamped, tamper-evident record that you owned the file at a specific moment.
Users also get a full photo vault with visible-watermark customization (position, opacity, tiling), file sharing/revocation, and a risk dashboard that scores each photo's exposure.
How We Built It Frontend: React + TypeScript + Vite, using shadcn/ui components and TailwindCSS. All image processing that happens before upload (canvas watermarking and SHA-256 hashing) runs entirely in the browser via the Canvas 2D API and window.crypto.subtle.
Backend: Python FastAPI server running on port 3000 with local filesystem storage. The visible watermarking uses OpenCV (cv2.addWeighted for opacity blending, cv2.putText for text rendering with configurable position, tiling, and scale).
DCT Algorithm (DCT.py): We implemented a custom DCT steganography algorithm from scratch using scipy.fftpack. The image is converted to the YCrCb color space, then split into 8×8 blocks. For each block, we apply a 2D DCT, compare mid-frequency coefficients at positions [3,4] and [4,3], and encode one bit by forcing a relative ordering between them. Extraction reverses this; no key required, just the block structure. We deliberately chose mid-frequency coefficients: low frequencies are visible to the eye, high frequencies are stripped by JPEG compression, but mid-frequencies survive both.
API integration: Vite dev proxy routes all API calls from localhost:5173 to localhost:3000, eliminating CORS issues entirely in development.
Challenges We Ran Into
Finding the algorithm for watermarking and ways to implement it.
DCT block capacity vs image size: Our first implementation crashed on small test images. The watermark text gets encoded as binary (8 bits per character), and you need at least one 8×8 block per bit, it means a 10-character watermark needs 80 blocks minimum. We added a capacity check and minimum size validation.
- Merging two separate codebases mid-hackathon: Our frontend and backend were developed in parallel on separate branches, with different assumptions. The backend originally used AWS S3 presigned URLs, while the frontend expected direct multipart uploads. We rewrote the storage layer to use a local filesystem and aligned the API contracts.
- Invisible watermark that actually survives re-saves: Canvas alpha compositing at 1% is nearly invisible, but the pixel values do change. Getting the opacity low enough to be visually imperceptible while high enough to survive a JPEG re-save required careful testing.
- No .gitignore files: We accidentally staged node_modules/ (tens of thousands of files) before a critical push, which blocked the commit. We had to reconstruct the staging area from scratch.
Accomplishments That We're Proud Of
- DCT steganography that actually works. We implemented the full embed/extract pipeline from first principles (not a library), and it produces a clean round trip on real images.
- Zero-dependency client-side protection. The SHA-256 fingerprint and invisible canvas watermark both run entirely in the browser before any network call is made. Even if our backend goes down, your proof of creation still exists.
- Graceful fallback architecture. If the backend is unreachable, the app keeps working: uploads remain in browser memory, watermarking falls back to canvas rendering, and the UI displays a warning instead of crashing.
- Dual watermark strategy in one product. Combining a visible deterrent (OpenCV) with an invisible forensic marker (DCT) and a cryptographic timestamp (SHA-256) in a single upload flow is something most tools don't attempt.
What We Learned
- DCT is not just for compression Building the steganography algorithm from scratch gave us a real appreciation for why JPEG uses DCT: the frequency domain separates "stuff humans see" from "stuff they don't" in a way spatial pixel editing can't.
- The browser is more powerful than most developers realize window.crypto.subtle gives you hardware-accelerated SHA-256 with no dependencies. The Canvas 2D API can do complex image manipulation before a single byte leaves the device.
- Monorepo coordination is hard under time pressure. Having a clear API contract written before parallel development starts would have saved hours of integration debugging.
- Local-first beats cloud-first for hackathon demos. Cutting AWS S3 for local filesystem storage removed an entire class of credential, latency, and configuration problems with zero UX impact for a demo setting.
What's Next for PhotoGuard
- Real cryptographic signing. Replace the in-memory auth with a proper per-user key pair. Sign the SHA-256 hash with the user's private key so that proof of creation is cryptographically verifiable by anyone, not just us.
- Blockchain timestamping. Anchor the image hash to a public blockchain (Ethereum or Bitcoin via OP_RETURN) for immutable, third-party-verifiable proof of existence at a specific time.
- Watermark extraction tool. A public-facing "verify ownership" page where anyone can upload a suspected stolen image, and we'll extract and decode the embedded DCT watermark to reveal the original owner.
- Stronger DCT robustness. Current implementation breaks under heavy cropping or resizing. Adding spread-spectrum encoding and error correction (Reed-Solomon) would make the watermark survive aggressive image manipulation.
- Browser extension. Detect unwatermarked images on social media and flag them directly to the creator in their feed.
Built With
- fastapi
- figma
- javascript
- python
- react
- tailwind
Log in or sign up for Devpost to join the conversation.