Stereogram Decoder
Choose a local PNG, JPEG or WebP autostereogram, or try the generated heart example. Set the repeating-strip offset, reveal differences and try the suggested offsets if the shape is unclear. Compare the original and reveal, then download the PNG. Your image stays in this browser.
Your workspace
Up to 16 million decoded pixels and a 128 MiB file. The image stays in this browser.
Each reveal pixel is the largest red, green or blue difference between a source pixel and the pixel one offset to its right. Where the pattern repeats at that offset the difference is zero, so the background drops to black (white when inverted) and a shape drawn with a different repeat stands out.
Current result and evidence
Choose a local stereogram image, or use the generated example to see how the reveal works.
- Image
- None chosen
- Offset
- Not revealed
- Masked columns
- None yet
How to create the heart stereogram
- Start with a 600 by 300 pixel image. Process each row from left to right and seed its first 90 columns with random colors.
- Use a background repeat of 90 pixels and an 8 pixel disparity reduction inside the heart. At each column x, copy the color from x minus 90 outside the heart, or x minus 82 inside it. Always copy an already written pixel in the same row.
- The heart mask uses u = (x - 300) / 72, v = (159 - y) / 72 and (u² + v² - 1)³ - u²v³ ≤ 0. A fixed random seed makes the dots reproducible.
- Save the complete recipe as create-heart.cjs and run node create-heart.cjs with Node.js. It writes heart.png using built-in modules. The supplied recipe produces the same original pixels as Use generated example.
- Load heart.png here and set offset 90, gain 1, threshold 0 and invert off. The background cancels; the heart differences appear one repeat to its left, and the last 90 columns are masked. Download PNG exports that difference reveal, not the original stereogram.
This is a two-depth creation recipe. Disparity changes the horizontal match separation; it is not a measurement of physical depth. More complex depth maps need correspondence and occlusion handling.
// Save as create-heart.cjs, then run: node create-heart.cjs
const fs = require('node:fs');
const { deflateSync } = require('node:zlib');
const width = 600, height = 300, repeat = 90, disparity = 8;
const rgba = new Uint8Array(width * height * 4);
let seed = 0x5eed2026;
function random() {
seed = (seed + 0x6d2b79f5) >>> 0;
let t = Math.imul(seed ^ (seed >>> 15), seed | 1);
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
}
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const u = (x - 300) / 72, v = (159 - y) / 72;
const r = u * u + v * v - 1;
const heart = r * r * r - u * u * v * v * v <= 0;
const separation = repeat - (heart ? disparity : 0);
const i = (y * width + x) * 4;
if (x < separation) {
rgba[i] = 30 + Math.floor(random() * 90);
rgba[i + 1] = 90 + Math.floor(random() * 120);
rgba[i + 2] = 120 + Math.floor(random() * 120);
} else {
const partner = i - separation * 4;
rgba.set(rgba.subarray(partner, partner + 3), i);
}
rgba[i + 3] = 255;
}
}
// PNG rows: filter byte 0, followed by the RGBA pixels of that row.
const rows = Buffer.alloc(height * (width * 4 + 1));
for (let y = 0; y < height; y++)
rows.set(rgba.subarray(y * width * 4, (y + 1) * width * 4), y * (width * 4 + 1) + 1);
function chunk(type, data) {
const name = Buffer.from(type), out = Buffer.alloc(data.length + 12);
out.writeUInt32BE(data.length); name.copy(out, 4); data.copy(out, 8);
let crc = 0xffffffff;
for (const byte of Buffer.concat([name, data])) {
crc ^= byte;
for (let bit = 0; bit < 8; bit++) crc = (crc >>> 1) ^ ((crc & 1) ? 0xedb88320 : 0);
}
out.writeUInt32BE((crc ^ 0xffffffff) >>> 0, out.length - 4);
return out;
}
const header = Buffer.alloc(13);
header.writeUInt32BE(width); header.writeUInt32BE(height, 4);
header[8] = 8; header[9] = 6; // 8-bit RGBA
fs.writeFileSync('heart.png', Buffer.concat([
Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]),
chunk('IHDR', header), chunk('IDAT', deflateSync(rows)), chunk('IEND', Buffer.alloc(0))
]));Practice viewing the heart
Use generated example, then choose Original only for an uncluttered image. Keep its size steady, look beyond it and let adjacent repeats overlap. Switch to Reveal only to check the silhouette, then return to Original only. Reset settings restores offset 90 for the generated image. The reveal offset changes a comparison, not the original image's depth.
The moving example lets you pause or step through one frame at a time and compare disparity reductions of 4, 8 or 12 pixels. A smaller reduction changes the matching separation; it does not guarantee that the image is easier for a particular viewer. Human viewing difficulty has not been measured.
Paused at frame 4. Background separation 90 px; heart separation 82 px. Reduced motion or unavailable motion preferences: use Next frame. This original example does not load or export GIF or video.
- Background drops to zero difference; a shape with a different repeat stands out
- Marks reveal columns s and s - d, nothing else
- Transparent in the PNG, hatched on screen, never read as depth
Gain multiplies each difference and caps it at 255. Threshold then sets any value below it to zero, which hides faint compression noise. Transparent source pixels are flattened onto white before comparing. The PNG keeps the pixel size and orientation of the compared image, in grayscale with transparent masked columns. Decoding and comparison run in a worker in this browser, and nothing is saved.
Common questions
- How do I find the right offset for my stereogram?
- Reveal once at any offset. That first reveal also scans every offset from 1 to half the image width (at most 1,024) across up to 32 evenly spaced rows, and offers up to three Try buttons for offsets whose average difference is a local low at least 25% below the median across all scanned offsets. You can also drag the slider or use the -1 px and +1 px buttons. At the right offset a repeating background cancels out, which is when a hidden silhouette is most likely to stand out.
- Why is a strip on the right of the reveal hatched?
- Each reveal column compares a pixel with the one a full offset to its right. The rightmost columns, as many as the offset, have no partner inside the image, so they are masked: transparent in the PNG and hatched on screen. They are not depth information.
- Why does the silhouette look shifted or doubled?
- Reveal column x reports the difference between source columns x and x plus the offset, so a change at source column s marks reveal columns s and s minus the offset. That is why a silhouette can appear up to one offset to the left of where it sits in the source image, or with doubled edges.
- Why is my reveal noisy?
- If the offset does not match the repeat width, the background no longer cancels; try the suggested offsets or step by 1 px. JPEG compression adds small differences everywhere, so raise the threshold (24 by default) to set faint values to zero, or lower the gain (3 by default). Stereograms built from photos, complex textures or nonrepeating patterns may never give a clean silhouette with this method.
- Does it rebuild the 3D depth map?
- No. It shows where the pattern stops matching itself at one horizontal offset, which can outline the hidden object. It does not compute depth, recognize objects or test your eyesight.
- What does the downloaded PNG contain?
- A grayscale PNG with the same pixel width and height as the compared image. Bright pixels (dark ones with invert on) are differences after gain and threshold, and the masked columns on the right are transparent. The file name records the offset, for example stereogram-reveal-offset-140.png, and Copy settings copies the offset, gain, threshold and invert values as text.
- Is my image uploaded?
- No. The file is read, decoded and compared in your browser, with the heavy work in a web worker, and nothing is saved. Images up to 16 million decoded pixels, 16,384 pixels per side and 128 MiB are accepted; transparent pixels are flattened onto white before comparing.
- What is a stereogram, and how does it work?
- A stereogram creates an impression of depth by giving each eye a slightly different view. Some use two separate images. An autostereogram puts both views into one repeating picture: your eyes pair repeated details at different horizontal separations and interpret those differences as depth. This tool compares shifted pixels in a single autostereogram; it does not combine a stereo pair.
- How do I see the 3D image without the decoder?
- For a parallel-view autostereogram such as the generated heart example, hold the image steady and look through it as if focusing on a point farther away. Let matching repeated details overlap, then bring the pattern into focus while keeping that alignment. A cross-view image instead asks you to focus in front of the picture; using the opposite technique can reverse the apparent depth. The difference reveal is another way to inspect a silhouette, and does not confirm whether you can see the 3D effect.
- How can I create a stereogram?
- Open How to create the heart stereogram for a complete local Node.js recipe that writes heart.png, matching the generated example pixel for pixel. Its 600 by 300 image repeats every 90 pixels in the background and every 82 inside the heart: 90 minus an 8 pixel disparity reduction. The guide explains the shape mask, left-to-right copying and how to check the result here at offset 90, gain 1 and threshold 0. It is a reproducible two-depth recipe; this decoder has no custom maker controls and its Download PNG button exports a difference reveal.
- Can I practice with a moving stereogram?
- Open Practice viewing the heart for an original 600 by 300 moving example. Play starts a six-second sequence at four frames per second; Pause and Next frame let you inspect it, and the disparity reduction can be 4, 8 or 12 pixels. Motion starts only on request, stops when the page is hidden and is disabled for reduced motion, with manual stepping available. The same section gives a static viewing exercise. Viewing difficulty has not been measured. GIF and video uploads, animated downloads and temporal decoding are unavailable.
Computes shifted-pixel differences to reveal some autostereogram silhouettes; it is not full 3D reconstruction, a guaranteed solution for every stereogram, object recognition or a visual health test. An incorrect offset and complex or nonrepeating patterns can produce noise. Invalid nonoverlap columns are visibly masked, not interpreted as depth.