Generate a Magic Eye-style autostereogram from a text prompt.
As far as I can tell, this is the first fully generative text-to-stereogram tool. Most others need you to supply your own image and only render it in a preset pattern; this one generates the hidden subject, the depth map, and a themed surface pattern from one text prompt.
prompt
→ rewrite (reshape the prompt; write a matching tile prompt)
→ image ∥ tile (subject + seamless pattern, in parallel)
→ depth (in-browser Depth Anything by default)
→ prepareDepth (histogram stretch, ground-ramp control, continuous subject)
→ render (oversampled SIRDS + pattern strip)
→ plate (+ fusion-guide dots)
npm run dev mocks the AI calls with local fixtures. No API keys are required for that path. Live providers are opt-in (see below).
npm install
cp .env.example .env
npm run dev # Vite + mocked AITo run the Netlify function runtime locally (still mocked unless live flags are set):
npm run dev:netlifyOpen the URL Vite prints. Type a term and press Enter. Add ?debug for the floating debug panel (see below).
Default local path needs no keys — mocks only. To hit real providers,
set flags + keys in .env (see .env.example) and run npm run dev:netlify.
npm run dev always mocks, even if live flags are set.
Primary live stack (what production uses):
| Step | Provider |
|---|---|
| Rewrite | Anthropic |
| Subject + tile images | fal.ai (IMAGE_PROVIDER=fal-flux) |
| Depth | In-browser Depth Anything (no paid API) |
Replicate is a fallback, not the default path. It remains wired for:
- Image gen if you set
IMAGE_PROVIDER=replicate-flux(instant rollback from fal) - Server depth if in-browser depth fails, or you opt in with
?serverdepth - Optional keep-warm pings when server depth is live
| Flag | Effect |
|---|---|
USE_LIVE_APIS=1 |
all services live |
USE_LIVE_IMAGE=1 |
image gen live (fal or Replicate, per IMAGE_PROVIDER) |
USE_LIVE_DEPTH=1 |
server depth live (Replicate; unused on the default client-depth path) |
USE_LIVE_REPLICATE=1 |
shorthand for image + server depth live |
USE_LIVE_REWRITE=1 |
Anthropic rewrite |
Live calls also run in production (where the live flags and keys are set in the Netlify dashboard).
Append ?debug to show a floating bottom-right debug panel (minimized by default; expand with the ▸ debug toggle). End users only get Reveal / Hide and Download — everything else lives here:
- Views — plate (stereogram), layers (source + depth + tile), ghost (client-side decode of the hidden form; free, no API)
- Decode QA — background period, subject fraction / coherence / depth, flat verdict
- Render params — live readout of the URL flags below (eye sep, tile scale, depth path, etc.)
- Shaped prompt — the rewritten subject prompt after the rewrite step
- Term atlas — link to the public
/atlaspage (see below)
| Flag | What it does |
|---|---|
?debug |
Mount the debug panel |
?fresh |
Bypass Stage-1 / Stage-2 cache reads; force a full regeneration (still writes back) |
?dots |
Force the random-dot renderer instead of the pattern tile (A/B) |
?serverdepth |
Use Replicate server depth instead of in-browser Depth Anything |
?eyesep=<in> |
Override eye separation in inches (clamped 1.5–5); widens/narrows the pattern period |
?tilescale=<n> |
Motif density: how much the tile is pre-shrunk before strip crop (1–6, default 2.5) |
?relief / ?norelief |
Force subject-relief stretch on/off (auto-on for person rewrites) |
?shading / ?noshading |
Force shading-relief injection on/off (same auto rule) |
?nosmooth |
Skip the soft-edge / terrace-damping blur on subject depth |
?calibrate |
Diagnostic depth card (known squares) for viewing / fusion checks |
Flags combine as usual, e.g. ?debug&fresh&dots. Renderer / depth flags take effect on cache hits too (hits re-render locally); only ?fresh re-runs the paid upstream calls.
Public page at /atlas (also linked from the debug panel). Lists search terms that have landed in the shared Stage-2 cache — successful gens other visitors can hit without waiting. Sort by recent or by hit count; each row shows the cached subject (+ tile when present) and first/last/hit meta. The shaped rewrite prompts are not listed.
src/
App.tsx app state + UI
Atlas.tsx / Atlas.css Term Atlas page (/atlas)
stereogram/
stereogram.ts SIRDS (random-dot fallback)
patternStereogram.ts texture-mapped renderer
postprocess.ts prepareDepth()
depthFromImage.ts image → Float32Array depth
depthBrowser.ts in-browser Depth Anything
decode.ts plate decode + flat check
lib/ API client, caches, share card
netlify/functions/ rewrite / image / depth / cache / warm / term-atlas
public/mock/ mock fixtures for local dev
public/demo/ landing plates
Stack: Vite, React, TypeScript (strict), plain CSS, Netlify Functions.
- Wait state. The generated subject image is the hidden content of the stereogram. The loading UI does not show it.
- Depth vs pattern. Pattern tiles make depth errors more visible than random dots. Change depth post-processing and the pattern renderer separately when debugging.
- Continuous subject depth. The default path keeps subject depth continuous. Hard depth planes can terrace smooth forms and make depth-estimator halos more visible.
- Fusion-guide dots. Two dots at the top of the plate sit one background period apart. Two dots becoming three usually means a correct one-period lock; four often means a two-period lock.
- Caching. Cached results store rewrite/image/depth/tile artifacts, not the finished canvas. A cache hit still runs the local render path. The Term Atlas lists Stage-2 cached terms publicly.
- Depth default. Depth Anything V2 Small runs in the browser via transformers.js (WebGPU when available). Server depth is a fallback.
Use divergent viewing: look through the plate from about a foot away until the two guide dots become three. If the hidden form sinks instead of coming forward, you are cross-viewing (same encoding, inverted depth).
Mike Deal, with help from Claude and Grok.
Not affiliated with or endorsed by Magic Eye Inc. Magic Eye® is a registered trademark of Magic Eye Inc.
MIT
