Hide secret messages — and entire images — inside ordinary photos. LSB steganography that runs entirely in your browser.
Named after the spy tradecraft technique: leaving hidden messages in plain sight, for retrieval without direct contact.
dead-drop lets you embed secret text or images inside any PNG by manipulating the least significant bits (LSBs) of each pixel's color channels. The result looks identical to the original — but carries a hidden payload readable only with the right tool and passphrase.
- LSB text encoding — hide text in 1–4 bits per channel (1 bit = completely invisible)
- Image-in-image (spatial) — hide an entire image inside another; the hidden image is spatially mapped pixel-to-pixel so it's visible as a ghost in the visualizer
- Channel-aware color encoding — RGB = full color recovery, single channel = grayscale, partial = tinted; your choice, stored in the header
- Multi-channel support — encode across any combination of Red, Green, Blue, and Alpha
- Alpha channel hiding — the most covert option; barely anyone inspects alpha
- AES-256-GCM encryption — optional passphrase encrypts text payloads before embedding (PBKDF2, 100k iterations)
- Auto-detect — scans all 60 channel/bit-depth combinations to find hidden data automatically
- Channel visualizer — four modes: raw channel, LSB amplified, LSB heatmap, and Reveal Hidden Image
- Zero backend — everything runs in the browser via Canvas API and Web Crypto. No data is ever uploaded.
- Demo images — pre-baked samples with hidden messages ready to decode
Every pixel is stored as 4 bytes: Red, Green, Blue, Alpha (0–255 each).
Changing the least significant bit of a channel shifts its value by at most 1 — imperceptible as a color change, but enough to carry one bit of information per channel per pixel.
A 800×533 image has ~426K pixels. At 1 bpp across 4 channels that's ~213KB of capacity — enough for a long text message or a small hidden image.
[0xDE][0xAD][0x01][length: 4 bytes BE][message bytes…]
Header (8 pixels, Red channel, full byte):
px0: 0x5D px1: 0xD5 (magic)
px2-3: hidden width (BE)
px4-5: hidden height (BE)
px6: bits per channel
px7: channel bitmask (bit0=R, bit1=G, bit2=B, bit3=A)
Then: hidden pixel[x,y] → carrier pixel[x+8,y] LSBs
The spatial format maps hidden pixels directly onto carrier pixels at the same coordinates — so the hidden image is visible as a ghost in the LSB visualizer.
- Derive AES-256 key via PBKDF2 (SHA-256, 100k iterations, 16-byte random salt)
- Encrypt with AES-GCM (12-byte random IV)
- Store
[salt(16)][iv(12)][ciphertext]as the payload - Embed into image LSBs
- Encode tab → upload a PNG carrier image
- Select Text message, type your secret
- Choose channels and bit depth; add passphrase if desired
- Hide Message → download the output PNG
- Encode tab → upload a carrier PNG
- Select Hidden image → upload the image to hide
- Choose channels — RGB = full color, one channel = grayscale
- Hide Message → download output
- Load into Visualize → Reveal hidden image to see it materialize
- Decode tab → upload an encoded PNG
- Hit Auto-detect — it scans all combinations and lists what it finds
- Click a result to decode it instantly
- Enter passphrase if the message was encrypted
Upload any image and switch between:
- LSB amplified — see where data lives in each channel
- LSB heatmap — combined view across all channels
- Reveal hidden image — decodes a spatial image payload directly in the visualizer
JPEG compression is lossy — it destroys the precise LSB values. Always download and share as PNG to preserve the hidden payload.
dead-drop/
├── index.html # Single-page app
├── css/style.css # Dark theme UI
├── js/
│ ├── steganography.js # LSB + spatial encode/decode engine
│ ├── crypto.js # AES-256-GCM via Web Crypto API
│ ├── visualizer.js # Channel rendering, heatmap, spatial reveal
│ └── app.js # UI logic
├── samples/
│ ├── demo-1.png # Mountains — hidden text in Red channel
│ └── demo-2.png # Forest — hidden text in Alpha channel
├── favicon.ico / favicon.png / favicon-192.png
├── dead-drop-hero.png # Portfolio hero image
└── generate-samples.js # Node script to regenerate demo images
No build step — plain HTML/CSS/JS with ES modules.
npx serve .
# or
python3 -m http.server 8080Open http://localhost:8080. ES modules require a server — can't open index.html as file://.
MIT