Sadly another shot in the arms race that captchas started which just leads to increased inaccessibility.
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...
One side i really like it - i also love to play around with funny ideas - but have to say if i would read more than like 2 sentences with that font i'd throw up xD
Why would they be trained to read a research experiment that fundamentally goes against the way they perceive? They can't train on this technique, they can only postprocess it into a form they can perceive.
It has bugs with long words: I typed "MARRY AND REPRODUCE". That was the only try that got the last word on a single line, but with too much space between U and C.
If 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.
I'm colourblind and this was very difficult to read. If it's the directions to the resistance hq, I'd put in the effort. If it's the manifesto, I just wouldn't read it.
You can also write using sound based/compressed 'text message' dialect: unless a real human is reading, automated watching tool should have a hard time (until coded/ML-ed on such dialects I guess)
I've had the same idea recently, and even set up a similar page to experiment with different speeds and noise types. I've had the idea to set up a message board where the font is basically 'GhostFont'. However, in my experiments, I've noticed that the biggest issue is that this only works for larger font sizes. If the text is as small as, for example, on HackerNews, it will become borderline unreadable.
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 pasted a screenshot of the default text ("GHOST FONT") into ChatGPT 5.6 Sol, told it to read it, and without further instruction it chewed on it for awhile before coming back with:
An interesting experiment. I suppose that if you make things like CAPTCHAs too hard to do, we'd end up struggling as well. I can't imagine Ghost Font would be a good fit.
When I gave Fable a screenshot it found the GHOST portion of GHOST FONT. Based on pixel density via some python code apparently - https://imgur.com/a/m3c801F
Hackernews is very good at finding the decoy message. This is as funny as thedailywtf posters who post "code snippets" to fix some other code snippet, and every single code snippet is wrong.
Took me a long time to realise that "Written In Ghost Text" wasn't actually the text I was meant to be reading, and that was only the decoy message.
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.
Funny, for me it is exactly the opposite: I can read the actual text very easily, but the “Written in Ghost Text” is barely perceptible to the point I would have completely missed it, if it were not for the comment pointing it out here.
> ”Took me a long time to realise that "Written In Ghost Text" wasn't actually the text I was meant to be reading, and that was only the decoy message.”
Wait, what? Seriously? That’s the only text I can see. Am I an AI?
Ah, I can't see magic eye pics. The secondary text is invisible to me as well. An Ai would probably be able to get it with multiple samples, so in the lonmg run it is more likely to be readable to AI than humans.
It depends on how big/dense is your screen, high frequency message is actual text while low frequency message is decoy text. Probably so that it shows when frames are averaged together without checking actual motion vectors. It is similar effect like that image showing either young woman or old man depending on distance you are viewing it from (or other one with either Einstein or Marylin Monroe)
"find out with opencv what the hidden message is."
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.
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
I had thought to use homographs. Sadly, all the models I tried were able to decode something like:
"フㄖ乇ㄚ ᗪㄖ乇丂几'ㄒ 丂卄卂尺乇 千ㄖㄖᗪ"
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?
There's garden path sentences, where the sentence is phrased in such a way as to cause you to misparse the sentence when you first read (e.g. "The old man the boat"); but those typically confuse humans (I'm not sure how effective they are on LLMs).
Technically it's not a font, because font needs to be still. Analogy: if I took photo after book was closed would we say that font cannot be read by a camera?
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
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).
I haven’t tried, but it looks like you could trivially solve with optical flow?
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.
103 comments
[ 3.2 ms ] story [ 86.3 ms ] threadIt'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...
So...usefulness?
- "This game disappears if you pause it": https://youtu.be/Bg3RAI8uyVw
- "Illusion: If You Pause, The Image Will Disappear": https://youtu.be/ZqGfb_Vlrig
And as a bonus reference - this all reminds me a lot of the book "There Is No Antimemetics Division".
If 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.
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.
HackerNews never disappoints
https://ibb.co/WWMSXQkQ
(so either I am AI at a level less than Opus 4.8 or just all-round defective as a human)
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.
Wait, what? Seriously? That’s the only text I can see. Am I an AI?
[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.
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:
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"
if __name__ == "__main__": main()lol. Barely.
"フㄖ乇ㄚ ᗪㄖ乇丂几'ㄒ 丂卄卂尺乇 千ㄖㄖᗪ"
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/
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:
: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).
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.