LiquidFree

AI Typo Morph

Every 3 s, the word on screen scrambles into randomly drawn characters, then settles left to right, one letter fixed every 100 ms, into the next word: a "decryption"-style decode in an indigo-pink gradient. CSS and 1.9 KB of JavaScript, no AI model, word list in the HTML, a direct switch when the user reduces motion.

JSTypoMorph

Updated

DESIGN
Hover or click the scene to interact

This effect is free — the Liquid category contains 31 effects total, including 6 free. Effect.Labs has 840 vanilla effects. Explore the category →

Usage examples

Blind tasting · Thursday evening

Wine no. 4 was…

WINE NO. 4
23 votes61% guessed the Loire4 found the vintage

① The mystery wine revealed — a blind tasting evening at a wine shop

WhenAfter the votes, the screen at the counter shows "Wine no. 4 was…"; the name decodes letter by letter into "CHINON 2019", above the voting results.
WhyLetters settling one by one build a short suspense; Text Morph's fade would reveal everything at once.
Settingsdata-words="WINE NO. 4, CHINON 2019"; 50 px serif word, champagne gradient (#f7e3b0 → #d4a24c) on a wine-red background; for a one-time reveal, replace the list with the wine's name alone after the first decode.
La Fabrique · community spaceWelcome

Wi-Fi network TIERS-LIEU · today's code:

????-????

The code changes every morning at 8 am · ask at the bar if needed

② Today's Wi-Fi code — the welcome screen of a community space

WhenOn the screen by the entrance, "Wi-Fi network TIERS-LIEU · today's code:"; the code goes from "????-????" to "CAFE-7432" as it decodes.
WhyLetters settling one by one draw the eye to the new code without blinking; a meaningless string suits scrambling, a readable word (Text Morph) does not.
Settings46 px monospace font (the line does not move during the scramble), original gradient in a dark plate on a cream background; data-words="????-????, CAFE-7432".
Last contact
F4KXY

14.074 MHz · FT8
−12 dB · JN18
14:32 UTC

TimeCallsignBandSignal
14:27OZ1QRV20 m−18 dB
14:19I2WXQ20 m−09 dB
14:06LA9ZQK40 m−21 dB

③ Callsign of the station heard — a radio club's traffic log

WhenIn the "Last contact" banner, the station's callsign decodes letter by letter with each new station heard, above the log of previous contacts.
WhyA callsign is a meaningless string of letters and digits: the scramble settling into place suggests a signal being decoded.
Settings44 px monospace font, sky to pink gradient (#7dd3fc → #f0abfc); three callsigns in data-words; frequency, mode and time on the right, contacts table below.

How it works

The word is a <div class="ai-typo-word"> centered in .ai-typo-morph: 2.5rem, weight 800, 2 px of letter spacing, painted with an indigo, violet, pink gradient (#6366f1 → #a855f7 → #ec4899) clipped to the letter shapes (background-clip: text). The word list sits in its data-words attribute, comma-separated; the first word is written in the HTML, so it shows without the script.

Every 3 s, the script reads data-words again, takes the next word and starts the decode: every 50 ms, it rewrites the whole text. The letter at position i is fixed as soon as the pass counter exceeds 2 × i: one more letter becomes right every 100 ms, left to right; the others are drawn at random from 66 characters (A to Z, a to z, 0 to 9, @ # $ %), and spaces stay spaces. After length × 2 + 5 passes, the exact word is set: 0.85 s for a 6-letter word, 1.35 s for "CHINON 2019". The only randomness is that character draw (Math.random): the catalog name "AI" covers no model and no artificial intelligence.

The script handles every .ai-typo-word on the page, each with its own list and timer, and does nothing while the word is off screen (IntersectionObserver) or the tab is hidden. Since the list is read again at each change, the page can modify it: a one-word list stops the cycle on that word. The drawn characters do not have the same width as the final letters: in a proportional font, the word breathes a little during the decode; a monospace font keeps the line still, useful for a code or a callsign. Measured in Chromium 151, Firefox 153 and WebKit 26: DESIGN, CREATE, MORPH, DREAM, in order, each decode finished before the next one.

Accessibility

  • Reduced motion: under prefers-reduced-motion: reduce, the word switches at once every 3 s, with no scrambled character. Measured in all three engines: over 7 s, only complete words appear.
  • Screen readers: during the decode, the element's actual text is a string of random characters, rewritten every 50 ms. The element is not an aria-live region, so nothing is announced; but a screen reader reading it at that moment hears gibberish. If the word carries information (a code, a name to reveal), put aria-hidden="true" on the animated element and write the final word in visually hidden text, updated once the decode is done.
  • Flashing: the characters change fast but with no flash or brightness alternation, and the scramble lasts under 1.5 s per word.

Browser compatibility

CSS (background-clip: text) + 1.9 KB of JavaScript (setInterval, IntersectionObserver, matchMedia), no dependency. Measured in Chromium 151, Firefox 153 and WebKit 26: same word order, same pace of one letter every 100 ms, same direct switch under reduced motion; off-screen pause checked in Chromium and Firefox.

Chrome 58+✓ Full
Firefox 55+✓ Full
Safari 12.1+✓ Full
Edge 79+✓ Full
Mobile iOS 12.2+✓ Full
Android Chrome 58+✓ Full

Without JavaScript, the first word written in the HTML stays on screen, in its gradient. Without IntersectionObserver, the cycle keeps running off screen. Without background-clip: text (a rare case), the word shows on a gradient block: then remove -webkit-text-fill-color and give the text a color.

The code

Copy the three blocks into your page. No dependencies.

HTML
<div class="fx-0419-root">
  <div class="ai-typo-morph">
    <div class="ai-typo-word" data-words="DESIGN, CREATE, MORPH, DREAM, BUILD">DESIGN</div>
  </div>
</div>
CSS
.fx-0419-root,
.fx-0419-root *,
.fx-0419-root *::before,
.fx-0419-root *::after {
  box-sizing: border-box
}

.fx-0419-root {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 250px;
  overflow: hidden;
  position: relative;
  background: rgb(26, 26, 36);
  color: rgb(255, 255, 255);
  font-family: sans-serif;
}

.ai-typo-morph {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.ai-typo-word {
  font-size: 2.5rem;
  font-weight: 800;
  background: linear-gradient(135deg, #6366f1, #a855f7, #ec4899);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  letter-spacing: 2px;
}
JavaScript (fx-0419)
(function() {
  // AI Typo Morph: the word scrambles, then settles letter by letter
  // Words: data-words attribute, comma-separated (read again at each change)
  var reduce = window.matchMedia('(prefers-reduced-motion: reduce)');
  var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@#$%';

  document.querySelectorAll('.ai-typo-word').forEach(function(wordEl) {
    function readWords() {
      return (wordEl.getAttribute('data-words') || '').split(',')
        .map(function(w) { return w.trim(); })
        .filter(Boolean);
    }
    var currentWord = 0;
    var scramble = 0;
    var onScreen = true;
    if (!wordEl.textContent.trim()) wordEl.textContent = readWords()[0] || '';

    function morphTo(target) {
      clearInterval(scramble);
      // Reduced motion: the word switches at once, no scrambling
      if (reduce.matches) {
        wordEl.textContent = target;
        return;
      }
      var iterations = 0;
      scramble = setInterval(function() {
        var result = '';
        for (var i = 0; i < target.length; i++) {
          if (iterations > i * 2 || target[i] === ' ') {
            result += target[i];
          } else {
            result += chars[Math.floor(Math.random() * chars.length)];
          }
        }
        wordEl.textContent = result;
        iterations++;
        if (iterations > target.length * 2 + 5) {
          clearInterval(scramble);
          wordEl.textContent = target;
        }
      }, 50);
    }

    // Pause while the block is off screen
    if ('IntersectionObserver' in window) {
      new IntersectionObserver(function(entries) {
        onScreen = entries[0].isIntersecting;
      }).observe(wordEl);
    }

    setInterval(function() {
      var words = readWords();
      if (!onScreen || document.hidden || words.length < 2) return;
      currentWord = (currentWord + 1) % words.length;
      morphTo(words[currentWord]);
    }, 3000);
  });
})();

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
Words (HTML — data-words on .ai-typo-word) DESIGN, CREATE, MORPH, DREAM, BUILD Words, codes or phrases separated by commas; write the first one in the element. Read again at each change: a one-word list freezes the display on that word.
Pace (JS — 3000 in setInterval) 3 s between two words Keep it above the decode time (length × 100 ms + 250 ms): 2 s is enough for 6-letter words, 5 s leaves time to read a code.
Decode speed (JS — 50 ms and i * 2) one letter fixed every 100 ms i * 4 fixes a letter every 200 ms, for a longer suspense; i * 1 every 50 ms, for a lightning decode.
Drawn characters (JS — chars) A–Z, a–z, 0–9, @ # $ % '0123456789' for a counter or a numeric code, '01' for a binary look, symbols ('░▒▓█') for a more graphic scramble.
Font and gradient (CSS — .ai-typo-word) site font, 2.5rem, weight 800, indigo-violet-pink gradient font-family: monospace keeps the word from changing width during the scramble; the gradient can be replaced by any color or gradient.

FAQ

Is there any artificial intelligence in this effect?

No. The name comes from the catalog, but the script only draws characters at random (Math.random) and then fixes the target word's letters one by one, left to right. No model, no network call: the final word is always the one written in data-words.

How do I reveal a word only once, with no loop?

Write two words in the list, the placeholder text and then the word to reveal (data-words="WINE NO. 4, CHINON 2019"). After the first decode, replace the list with the revealed word alone (el.dataset.words = 'CHINON 2019'): with a single word, the script changes nothing more. To trigger the reveal on click instead of after 3 s, start with a single word in the list and add the second one on click.

How does it differ from Text Morph (fx-0405)?

Text Morph goes from one word to the next with a blurred fade: you only ever see whole words, readable at every moment. AI Typo Morph shows the transition: the word scrambles into random characters, then rebuilds letter by letter. Choose AI Typo Morph for a code, a name to reveal or a "signal" mood; Text Morph for words that must stay readable (languages, jobs, products).