Ghost Font: A font that humans can read but AI cannot
mixfont.com
mixfont.com
I can barely read the actual message, and it's about as "readable" to me as the Magic Eye 3D pictures. Actually I think I have a headache from looking at it on a mobile screen.
As a research idea it's cool though. But I do wonder if/when AI models will figure out how to decode it - I imagine a bit of additional prompting would get them there.
My phone would have been zooming out the browser window, and making the dots even tinier, but the phone is HiDPI so it would have still preserved the dots. My eyes are middle-aged and probably starting to do the same kind of median-blur effect that models do when they resize an image. That's my current guess for why I can see the decoy more clearly on mobile.
If that's the case, then this trick will stop working as vision models approach pixel-perfect vision, instead of the current resizing that they do. Pretty cool as steganography though.
Wait, what? Seriously? That’s the only text I can see. Am I an AI?
But a still frame doesn't contain the information necessary to see the actual message, so when it's paused, I can only barely see something which I suppose says something along the lines of "written in ghost text".
This is super useful; I’ve always wanted my information to be illegible when scaled…
I look forward to being able to see C-beams glitter in the dark near Tannhäuser Gate.
[edit] http://lentitaliane.it/blog/wp-content/uploads/2014/10/illus...
I’m guessing it is a one-shot task. On the other hand, it’s illegible to a large percentage of humans.
I always thought that once computers got better at solving captchas than humans, they would go away.
I couldn’t have been more wrong.
It seems to me like it should be easy enough to take Ghost Font, apply normal video compression techniques, and analyze the compressed signal to recover the visual outline of the letters, which you would then analyze with OCR (or an AI I guess ...). In other words, a novel CAPTCHA technique but not necessarily "fundamentally more difficult" than existing CAPTCHA techniques, once the cat-and-mouse game gets going.
So the next time you're asked to click the pictures with the bicycles or traffic lights? Please do so before it's too late.
(/s, if that's necessary)
https://fingswotidun.com/images/GhostFont_2_samples.jpg
https://fingswotidun.com/images/GhostFont_b_2_samples.jpg
An AI would be able to take consecutive frames rather than hamfistedly pressing the screenshot button, then accumulate enough samples to clearly make out the text from a clip shorter than the time it would take for a human to read it.
The real message is "only a human can read this" and your filters didn't get these words.
(But I think there should be a way to catch them)
Am I… am I an AI?
"No problem" as in using temporal analysis with optical flow and vertical-displacement maps to estimate how the image moved, and combine those into a motion map with increased contrast to see the text. I didn't give it any instructions though, just asked it what it said.
What this is, is a message that is more reliably readable by a sufficiently good AI than it is by all humans.
As also stated in the article.
Maybe AI will take over after all, just not due to the reasons we think.
Inspected video metadata, extracted frames, and analyzed content
Detected and analyzed periodicity in stereogram image data
Blurred, downsampled, and analyzed image frames
Computed frame pixel shifts and cross-correlation
Checked OpenCV installation, optimized and analyzed optical flow processing
Analyzed motion for text
Estimated vertical displacement and processed image flow
Refined text extraction
Processed optical flow and analyzed imageEdit: interestingly gpt5.6 needed a slightly longer video and got it too
WHAT HAPPENS IN VEGAS
STAYS IN VEGASEDIT: On second look, the static screenshot does say "WRITTEN IN GHOST FONT".
I didn't go into it with an expectation, or a hypothesis. The experiment was very low effort, and had very low cost, and it was the loose equivalent of throwing some shit at the wall to see if any of it sticks.
The result was my own amusement. This result wasn't any more unexpected than any other result would have been.
What did you expect from a screenshot of obvious noise, yourself?
The text is a video. Every frame contain random dots, so an individual frame by itself doesn't contain the intended message
This "font" exploits the fact that current-gen frontier models will process video one frame at time, but each frame is noise, so by looking at frames in isolation doesn't reveal anything
Then, they add a hidden message to each frame just so that the agent report something and stop trying (because if the agent tried to correlate between the frames, they could discover the trick)
But if you pass just a frame, there is no message. Just the noise plus the decoy
https://i.imgur.com/CgtyGjl.png
From a single frame you can definitely identify boundaries because the dots are sliding and get truncated.
So there are two texts, one decoy (which you can barely see in a single frame but becomes more clear if you average between frames) and an actual text, which disappears in single frames or averaged ones.
Just a single frame (any frame). After that any modern LLM can read it (if not - single dilate step helps). Funny enough, Ghost Font is a font that machine can read much better than human.
So each frame can't be 100% random and it must show some outline - but my guess is that it's the outline of the decoy, and unrelated to the outline of the actual message
And per what was reported, it's already pretty hard for the agent to figure out the decoy. After they finally crack it, they consider the problem solved
I think it's genius. The only problem is that it's trivially defeated by some tool written specifically to read it. So it defeats current-gen agents but if it becomes popular (eg. used by captcha services), future LLMs will just write a small Python script to read it
HackerNews never disappoints
I did this in 20 lines of code (checking only vertical perturbations), and this is what I get with subtracting frame 7 from frame 1:
This ghost font thing is a pretty solid trick
So...usefulness?
Took a picture (only a single frame) and a 1s movie and threw it toward GPT 5.6 Sol (High):
Frame took 9m30s to decyper and GPT 5.6, it returned: WRITTEN IN GHOST FONT. Weird because I can only see "GHOST FONT" on the demo... but extracted data from image (I saw the highlited one) definitely looks like the "Ghost Font".
--
Video is more amusing, because after 3m GPT 5.6 figured it's motion-defined and asked to run QuickTime. At one moment I got:
> The animation is a motion-defined illusion. I’ve confirmed there’s no readable static OCR layer; I’m decoding its optical-flow field so the letter shapes become explicit.
At 4m it got extracted motion image that was in shape of letters but analyzed for 9 more letters and returned (at 13m36s) "GHOST FONT"
--
So:
a font... - FALSE - not a font, but video effect
...humans can read... - FALSE - I can't read it from image (but AI can!)
...but AI cannot - FALSE - it can
:DEdit: https://imgur.com/a/SHlGu4O - work-in-progress images
It's a static decoy message independent from what you type in. You can see it if you take a long exposure pic of the screen (e.g. with your smartphone).
- "This game disappears if you pause it": https://youtu.be/Bg3RAI8uyVw
- "Illusion: If You Pause, The Image Will Disappear": https://youtu.be/ZqGfb_Vlrig
“Not just image. The sound also disappears when you pause”
Brilliant :)
And as a bonus reference - this all reminds me a lot of the book "There Is No Antimemetics Division".
> The decoy message serves as a final trick for a determined agent. When looking for a hidden message, it might first find the decoy message and think that that is the real embedded message in the video.
(so either I am AI at a level less than Opus 4.8 or just all-round defective as a human)
In this case, it's rendering from a hidden input element:
<input id="ghost-font-message" class="GhostFontLanding_messageInput__La9Ij" type="text" maxlength="36" placeholder="SECRET MESSAGE" autocomplete="off" spellcheck="false" value="HELLO HUMAN">
Gee.. yeah, that's entirely indecipherable.
If you want to erect security between AI and content, then ...
yeah, just don't bother. It's not even worth the trouble and it's quite likely impossible at scale anyway.
"A computer font or digital font is a digital data file containing a set of graphically related glyphs"
so it's not a font, humans can't read it and AI can.
And this thread is seemingly full of people claiming AI can read it while simultaneously sharing that AI could not read the actual message, only the decoy as demonstrated in TFA.
That’s 100% on the authors for failing to make the default main “hidden” text and the decoy easily distinguishable. The way this is set up is incredibly confusing.
It's a pretty crappy system if a portion of your intended audience can't even read it.
So once the technique is known by the model the font stops working as intended.
Out of curiosity can any of the LLM's:
- reach this [1] directly
- and decode it?
If so, which LLM's can reach it and which ones can decode it? It's just a static HTML file, nothing fancy. If any of them can reach it directly then I have homework to do. Created using:
echo -en '\nA I\n\nT e s t\n\n' | toilet --metal -f pagga --html | brotli --best -fncv > /dev/shm/test.html.br
[1] - https://nochan.net/test.htmlIf the string is empty, I can read "WRITTEN IN GHOST FONT" very faintly. I'm guessing that is a watermark Edit: Ah, it's decoy text. Of course.
EDIT: To be clear, I'm talking about the "Written in Morse Code" example, fully hallucinated text. The AI agents seeing a decoy message isn't as bothersome to me.
The ADA suits will be absolutely hilarious and honestly, I can’t wait.
It's interesting work for sure, but the end goal of separating out AI versus human consumers is tough. Indeed, if there was a lasting solution, that would be a substantial discovery that would quickly become very famous...
You just use a font that maps characters to random other ones.
So the actual source content of the doc is gibberish, but visually it looks like the correct words.
Of course a vision model could decipher it by rendering the doc, but maybe that's where this technology comes in, if you built a custom PDF renderer.
Then you'd have both obfuscated source content and rendered output.
"フㄖ乇ㄚ ᗪㄖ乇丂几'ㄒ 丂卄卂尺乇 千ㄖㄖᗪ"
However, I have noticed that voice assistants have a hard time understanding homonyms. Saying "bow" (as in to bow one's head) is often stored as "bow" (as in a bow and arrow). I wonder if there's a sufficiently complex sentence which is intelligible to humans but not to machines?
Relevant xkcd: https://xkcd.com/2793/
Edit: looks like yes, from the shared chats people are posting. But it’s interesting to think of communication schemes that require a temporal component so any single image is unreadable and can’t be beaten by long exposures or other tricks (otherwise persistence of vision displays would satisfy). A sort of physical anti copy/paste.
Now that "Ghost Font" will be in the training material, LLMs will go "this looks to be written in Ghost Font, and as such has two messages."
For the moment. This pattern is easy to code -- it relies on the premise that a character has an inside and an outside. Outside, a pattern ascends. Inside, the reverse. Based on that simple encoding idea, decoding will be equally simple.
I wonder if this is like the “rotate an apple” problem.
Edit: Reading other comments is there meant to be 2 messages?
Is the LLM really wrong if most humans and it agree that the decoy message is the real message?
It is still at fault if it gets the “real” message when a human that can actually read it is instructing the LLM?
That still makes it (well, a future version) potentially useful as a captcha if we hate our users but hate AI more.
Also
Default: Pure static.
Zoom in: Written in ghost font.
Zoom in more: Now you can read it.
How about writing or drawing stuff using optical illusions?
Shapes that not even human eyes can see, but the brain hallucinates: Shapes that seem to appear when you look straight at a pattern, or for a second after you look away from a pattern, or after you close your eyes, etc.
If you take a screenshot or a photo the image would just contain the same static pattern.
i.e. qualia-based "cryptography" :)
This font when screenshotted is just noise.
Changing the AI to take local motion data into account is probably not going to happen soon, due to taking quite a lot more computation, both within the AI model, and within the chromium instance being inspected.
It just looks like static on old tvs to me.
Furthermore, if AI can read this or not depends on how the text sequence is pre-processed. If AI only gets snapshots of the text, it will probably fail in decoding the text as every snapshot contains only white noise and such no information. However, if we calculate the Deltas between the animation frames, the text will become decodable by an AI, you probably don't even need LLMs or CNNs for this.
I found the bot living in a simulation!
What do I win? Where's my prize?
https://qri.org/blog/psycrypto-contest
https://www.youtube.com/watch?v=oD4nV0CMkBI
Of course, the psychedelic hidden message is reversible with some video processing techniques for everyone else to see. And calling it cryptography is a mis-use of the term. Still an interesting use of the effect.
I don't think "ghost font" will work as well as the author claims.
strong statement, I struggle to read it
lol. Barely.
Skill issue on promoter side.
Fable oneshotted it for me.
""" Reveal a motion-camouflaged message hidden in video noise.
How it works: The background noise scrolls vertically at a constant rate (a few px/frame), while the noise inside the letters does not follow that motion. Any single frame looks like pure static. The decode is:
1. Estimate the background's global motion between consecutive frames
with phase correlation (this is the "optical flow" step - the motion
is a pure translation, so one global vector suffices).
2. Motion-compensate: shift frame t+1 back by that vector so the
background lines up with frame t.
3. Take the absolute difference. The background cancels almost
perfectly; the letters (which don't move with the background)
light up.
4. Average the residual over a SHORT window of consecutive frame pairs
(long windows smear the letters, because the text itself drifts
slowly over time), blur lightly, and threshold with Otsu.
Usage:
python reveal_hidden_message.py input.mp4 [output.png]
"""import sys import cv2 import numpy as np
PAIRS = 5 # number of consecutive frame pairs to average (keep small!) BLUR_SIGMA = 6 # spatial blur of each residual, in pixels START_FRAME = 0 # where in the video to start
def load_gray_frames(path, count): cap = cv2.VideoCapture(path) frames = [] while len(frames) < count: ok, frame = cap.read() if not ok: break frames.append(cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY).astype(np.float32)) cap.release() if len(frames) < 2: raise SystemExit("Could not read enough frames from the video.") return frames
def main(): if len(sys.argv) < 2: raise SystemExit(__doc__) src = sys.argv[1] dst = sys.argv[2] if len(sys.argv) > 2 else "revealed_message.png"
frames = load_gray_frames(src, START_FRAME + PAIRS + 1)
h, w = frames[0].shape
acc = np.zeros((h, w), np.float32)
for i in range(START_FRAME, START_FRAME + PAIRS):
a, b = frames[i], frames[i + 1]
# 1) global background motion between the two frames
(dx, dy), response = cv2.phaseCorrelate(a, b)
dxi, dyi = int(round(dx)), int(round(dy))
print(f"pair {i}: background shift = ({dx:+.2f}, {dy:+.2f}) px, "
f"response = {response:.2f}")
# 2) motion-compensate frame b by integer (dxi, dyi), then
# 3) residual = |a - b_shifted| on the overlapping region
ys = slice(max(0, -dyi), min(h, h - dyi))
xs = slice(max(0, -dxi), min(w, w - dxi))
ysb = slice(max(0, dyi), min(h, h + dyi) if dyi < 0 else h)
# simpler: crop both to the common overlap
a_ov = a[max(0, -dyi):h - max(0, dyi), max(0, -dxi):w - max(0, dxi)]
b_ov = b[max(0, dyi):h - max(0, -dyi), max(0, dxi):w - max(0, -dxi)]
resid = cv2.GaussianBlur(np.abs(a_ov - b_ov), (0, 0), BLUR_SIGMA)
acc[:resid.shape[0], :resid.shape[1]] += resid
# 4) normalize + Otsu threshold + light cleanup
u8 = cv2.normalize(acc, None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8)
_, mask = cv2.threshold(u8, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
out = 255 - mask # black text on white
cv2.imwrite(dst, out)
print(f"wrote {dst}")
# optional: OCR if pytesseract is installed
try:
import pytesseract
text = pytesseract.image_to_string(out, config="--psm 6").strip()
print("OCR result:\n" + text)
except ImportError:
pass
if __name__ == "__main__":
main()Still could read https://chatgpt.com/share/6a5221f0-e3fc-83eb-bc15-74420002b6...