What is ChaosEdgeSteg?
ChaosEdgeSteg is a Python tool from the crypt0lith project β chaos-based, edge-adaptive LSB steganography. Instead of hiding bits in every pixel (which shows up under statistical analysis), it picks edge pixels via an adaptive Canny pass, orders them with a HΓ©non chaotic map keyed off your password, and writes the payload into the least significant bits of those locations only.
The scheme is non-blind and keyed: decoding needs three things β the
original cover image, the steg image (what you downloaded from X), and the
password you shared out of band. The cover reconstructs the edge pixel set; the password
drives the HΓ©non ordering. Wrong cover, wrong image order, or wrong password fails fast with a
bad password check on the CES header.
Upstream ships a CLI:
pip install git+https://github.com/crypt0lith/ChaosEdgeSteg.git
# embed files into a cover image
chaosedgesteg embed cover.png secret.txt -O steg.png
# extract from the steg image (needs the same cover + password)
chaosedgesteg extract cover.png steg.png -O extracted.zip
plastron's integration is byte-compatible both ways: a steg PNG made in the browser extracts under the desktop CLI, and an upstream-made steg image decodes in plastron.
Why this lives on plastron
The use case is specific: hide a https://plastron.ca/#f=<base64url> f-string
link β a shareable deep link into a live sketch (boids, kanban, whatever) β inside a small PNG
you can post to Twitter/X. If the image survives X's re-encode, anyone with the cover and password
can pull the link back out and click straight into your sketch.
ChaosEdgeSteg is not a new plastron application segment. It is a sheetapp document β a workbook of positioned cells where the algorithm, UI, password field, size guard, and decode logic are all ordinary spreadsheet formulas and a single Python source cell. Open it from the desktop like any other doc.
doc:chaosedgesteg.How Pyodide runs the upstream code
plastron already has a py compiler cel: paste Python, bind it with
=def('name', 'py', sourceCell), call the minted function from formulas. For
ChaosEdgeSteg we dropped the upstream henon.py and steg.py bodies
verbatim into cell A21, plus thin glue for PNGβBGR conversion and the
upstream zip container. Cell A22 binds it:
=def('chaosedgesteg.ces', 'py', A21).
The runtime is Pyodide v0.29.4 (CPython 3.13 in WebAssembly). On first open,
the browser downloads Pyodide core plus the packages the source actually imports β
numpy, opencv-python (cv2), pillow,
mpmath β roughly 30 MB from jsDelivr, cached after the first run. PNG I/O goes
through Pillow + numpy; OpenCV handles grayscale, bilateral filter, adaptive Canny, and LSB writes.
There is no reimplementation of the chaotic-map math β parity is upstream's own bytes.
One small native change made this possible: the py compiler now calls
pyodide.loadPackagesFromImports(source) before runPython, so any
import line in a py cell auto-fetches its wheel. ChaosEdgeSteg is the worked
example; before this, a cell that imported numpy would silently fail with
ModuleNotFoundError.
The algorithm is policy in cells; the only native additions are package auto-loading and
fs.pickToCel (turn a file-picker upload into base64 in a named cell). No new verbs
for steganography itself.
How to use it
You need three things to decode later: the original cover, the image from X, and the password. Keep the cover; share the password out of band.
1. Open ChaosEdgeSteg
Go to plastron.ca, open the origin desktop, and click π΅οΈ ChaosEdgeSteg. A workbook opens with two tabs on the right β π Decode and πΌ Embed.
2. Embed a link
- On the πΌ Embed pane, paste your
plastron.ca/#f=β¦link (or leave the default boids link). - Pick a PNG cover image. The sheet checks dimensions immediately.
- Set a password (default
SECRET_PASSWORD, same as upstream). - Wait for embedding β the first run loads OpenCV via Pyodide (a few seconds). When done, click β¬ Download steg PNG.
3. The β€680px rule (critical for X)
We ran a real round-trip test: seven steg PNGs posted to X, downloaded, decoded. PNGs with max dimension β€ 680px came back byte-for-byte identical β link recovered exactly. Images around 900px or larger were JPEG-recompressed and the LSBs were destroyed.
The Embed pane enforces this: if your cover is too big, you see
TOO BIG: 1200Γ630 β¦ and the sheet auto-downscales to β€680px before embedding.
If a downscaled copy was used, a second download link offers the cover actually used β
your decoder needs that exact file, not the original oversized one.
size OK β 680Γ357, β€680px. This cover will survive X's lossless re-encode.
4. Post to X
Upload the downloaded steg PNG to X as-is β no crop, no filter. This step is outside plastron;
the app deliberately does not fetch from twimg.com (CORS). Later, download the image
X served back.
5. Decode
- On the π Decode pane, upload the original cover and the image from X.
- Enter the password.
- The recovered text appears β as a clickable link when it is a URL. The sheet tries cover/steg in both orderings automatically.
- Click the link; your boids sketch (or whatever you hid) opens.
plastron.ca/#f=β¦ link recovered character-for-character and rendered as a clickable anchor.6. Cross-tool parity
A steg PNG made in plastron also opens in the upstream CLI:
chaosedgesteg extract cover.png plastron-steg.png -O recovered.zip
And the reverse: an image embedded with upstream's CLI decodes in the plastron workbook with the
same cover and password. The zip container (comment b"0", member 0.bin)
matches upstream's format exactly.
Under the hood (for the curious)
The embed path: grayscale the cover β adaptive Canny (bisection over thresholds on a
bilateral-filtered image to hit target edge density) β HΓ©non-map ordering keyed by a BLAKE2b-8
hash of the password β write LSBs at those pixels: a CES magic header plus
4-byte length, then payload bits. Extract re-runs Canny on the original cover to
reconstruct the pixel set, reads LSBs from the carrier, checks the header.
Formulas wire it together without incantations:
B7 = chaosedgesteg.ces('decode', B4, B5, B6)β decode sideB15 = chaosedgesteg.ces('embed', B13, B10, B14)β embed side (always uses the downscaled coverB13)B12 = chaosedgesteg.ces('size', B11)β dimension guardB13 = chaosedgesteg.ces('downscale', B11, 680)β auto-fit for X
Closing the workbook flushes and evicts every chaosedgesteg.* cell; the shared Pyodide
runtime stays cached at process level for the next py cell that needs it.
Status
Shipped in plastron-examples/origin as
apps/docs/chaosedgesteg.json, with a capstone e2e
(e2e/chaosedgesteg.mjs) that embeds a boids link, decodes it, and verifies
upstream parity on a fixture that actually round-tripped through X. A lighter sibling,
π΅οΈ NanoSteg, offers dependency-free LSB hiding for contrast β different
algorithm, incompatible by design.
This is a fun demonstration of plastron's "algorithm as spreadsheet policy" model: real scientific Python, visible in a cell, running in the browser, with every UX decision expressed as positioned formulas. Not a security product β steganography is obscurity, not encryption β but a neat way to smuggle share links through social image pipelines.
References
- crypt0lith/ChaosEdgeSteg β upstream chaos-based edge-adaptive steganography
- crypt0lith β project home
- Pyodide β CPython in WebAssembly; plastron pins v0.29.4
- plastron.ca β live demo; click π΅οΈ ChaosEdgeSteg on the desktop
- rheophile10/plastron β source