Sneakers Text Decode
How it works
Extracted from Infisical's React component in module 49899 of pages/index-4fc63f88002b15b5.js (HAR: https://infisical.com/_next/static/chunks/pages/index-4fc63f88002b15b5.js). The component is registered as s.A on the index page and applied to section-break labels (font-v2-mono uppercase), button text (cipher:true, cipherOnMount:true), and product-tab headings.
The character pool is exactly 76 chars: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%&*?/<>+=-. The timing parameters (all extracted verbatim) are: stagger = 70 ms, revealPerChar = 220 ms (the initial hold before char 0 resolves), glyphInterval = 80 ms (how often each pending glyph swaps), and maxDuration = 1500 ms (a cap that compresses stagger for long strings via r = (1500 - 220 - 200) / len). An IntersectionObserver with threshold: 0.4 triggers the animation once the element enters the viewport.
The reveal algorithm runs in a single requestAnimationFrame loop. On every frame it rebuilds the element's text content character-by-character: if elapsed time s >= index × r + 220 the real character is shown; otherwise the stored glyph is emitted and, if s - lastSwap >= 80, a new random glyph is sampled and the swap timestamp is updated. Spaces bypass the scramble entirely. When s exceeds the total duration, textContent is snapped to the final real string.
This rebuild is all DOM text (manipulating textContent of <div> and <span> elements) in a monospace font so glyph-width churn does not shift surrounding layout. The demo orchestrates an eyebrow → headline → secondary → eight grid-label cascade with 120 ms inter-label stagger, looping through three content sets drawn from real Infisical copy.
Code — copy & paste this whole file
<!doctype html>
<!--
Sneakers Text — Infisical cipher-scramble DOM rebuild
─────────────────────────────────────────────────────────────────────────────
Faithful port of Infisical's character-scramble effect (module 49899 in
https://infisical.com/_next/static/chunks/pages/index-4fc63f88002b15b5.js).
Every text node is a live DOM element whose textContent is updated
character-by-character on each requestAnimationFrame — no <canvas>.
Characters stay the same width because the font is monospace throughout.
Extracted parameters (exact, from module 49899):
• Character pool: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
0123456789!@#$%&*?/<>+=- (76 chars total)
• stagger = 70 ms (per-char reveal delay; compressed if text is long)
• revealPerChar = 220 ms (initial hold before first char locks in)
• glyphInterval = 80 ms (how often each unresolved glyph swaps)
• maxDuration = 1500 ms (total animation cap; stagger is recalculated)
• IntersectionObserver threshold = 0.4 (we trigger on load instead)
Completely standalone — no CDN, no web fonts, no external assets.
-->
<html lang="en">
<head>
<link rel="icon" href="data:,">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sneakers Text Decode</title>
<!-- a14y:begin head — machine-readable metadata; safe to delete when copying -->
<meta name="description" content="Characters scramble through random glyphs then snap left-to-right into readable text — like a cipher decode from the movie Sneakers.">
<link rel="canonical" href="https://fx.statico.io/effects/sneakers-text.html">
<link rel="alternate" type="text/markdown" href="https://fx.statico.io/effects/sneakers-text.md" title="Sneakers Text Decode (Markdown)">
<meta property="og:type" content="website">
<meta property="og:site_name" content="cool web fx">
<meta property="og:title" content="Sneakers Text Decode">
<meta property="og:description" content="Characters scramble through random glyphs then snap left-to-right into readable text — like a cipher decode from the movie Sneakers.">
<meta property="og:url" content="https://fx.statico.io/effects/sneakers-text.html">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Sneakers Text Decode">
<meta name="twitter:description" content="Characters scramble through random glyphs then snap left-to-right into readable text — like a cipher decode from the movie Sneakers.">
<script type="application/ld+json">{"@context":"https://schema.org","@graph":[{"@type":"SoftwareSourceCode","@id":"https://fx.statico.io/effects/sneakers-text.html#effect","name":"Sneakers Text Decode","url":"https://fx.statico.io/effects/sneakers-text.html","abstract":"Characters scramble through random glyphs then snap left-to-right into readable text — like a cipher decode from the movie Sneakers.","description":"Characters scramble through random glyphs then snap left-to-right into readable text — like a cipher decode from the movie Sneakers.","programmingLanguage":"HTML","codeSampleType":"full solution","runtimePlatform":"Web browser","codeRepository":"https://github.com/statico/cool-web-fx","license":"https://unlicense.org/","isBasedOn":"https://infisical.com/","mainEntityOfPage":"https://fx.statico.io/demos/sneakers-text","dateModified":"2026-07-27T17:24:00-07:00","inLanguage":"en"},{"@type":"BreadcrumbList","@id":"https://fx.statico.io/effects/sneakers-text.html#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://fx.statico.io/"},{"@type":"ListItem","position":2,"name":"Sneakers Text Decode","item":"https://fx.statico.io/demos/sneakers-text"},{"@type":"ListItem","position":3,"name":"Standalone source","item":"https://fx.statico.io/effects/sneakers-text.html"}]}]}</script>
<style>.a14y-doc{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}</style>
<!-- a14y:end head -->
<style>
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
html, body {
width: 100%;
height: 100%;
overflow: hidden;
background: #080a0e;
display: flex;
align-items: center;
justify-content: center;
font-family: 'Consolas', 'SF Mono', 'Monaco', 'Courier New', monospace;
-webkit-font-smoothing: antialiased;
}
/* ── Outer container ──────────────────────────────────────────────── */
.panel {
width: min(720px, 88vw);
display: flex;
flex-direction: column;
gap: 0;
user-select: none;
}
/* ── Eyebrow tag ──────────────────────────────────────────────────── */
.eyebrow {
font-size: 10px;
letter-spacing: 0.14em;
color: #a3e635;
opacity: 0.72;
margin-bottom: 28px;
min-height: 13px;
text-transform: uppercase;
}
/* ── Hero headline (cycles through phrases) ───────────────────────── */
.headline {
font-size: clamp(26px, 3.8vw, 46px);
font-weight: 600;
color: #edf2f7;
letter-spacing: -0.025em;
line-height: 1.1;
min-height: 1.15em;
}
/* ── Secondary line ───────────────────────────────────────────────── */
.secondary {
margin-top: 18px;
font-size: 12px;
color: #4a5568;
letter-spacing: 0.04em;
min-height: 16px;
}
/* ── Divider ──────────────────────────────────────────────────────── */
.divider {
margin-top: 36px;
border: none;
border-top: 1px solid rgba(255,255,255,0.06);
}
/* ── Label grid (2 columns, 4 rows) ───────────────────────────────── */
.label-grid {
margin-top: 0;
display: grid;
grid-template-columns: 1fr 1fr;
}
.label-cell {
display: flex;
align-items: center;
gap: 10px;
padding: 13px 0;
border-bottom: 1px solid rgba(255,255,255,0.06);
}
/* Right column cells get left padding and a vertical separator */
.label-cell:nth-child(even) {
padding-left: 28px;
border-left: 1px solid rgba(255,255,255,0.06);
}
.label-cell:nth-child(odd) {
padding-right: 28px;
}
/* Dot indicator — visible only when that cell's text is decoding */
.dot {
flex-shrink: 0;
width: 4px;
height: 4px;
border-radius: 50%;
background: #a3e635;
opacity: 0;
transition: opacity 200ms ease;
}
.dot.active { opacity: 0.55; }
.label-text {
font-size: 10px;
letter-spacing: 0.07em;
color: #718096;
text-transform: uppercase;
min-height: 13px;
/* Prevent layout reflow as random glyphs swap in/out */
white-space: nowrap;
overflow: hidden;
}
/* ── Reduced-motion: skip scramble, show plain text ─────────────── */
@media (prefers-reduced-motion: reduce) {
.eyebrow, .headline, .secondary, .label-text {
/* Content will be set by JS but animation won't run */
}
.dot { display: none; }
}
</style>
</head>
<body>
<!-- a14y:begin body — visually hidden description; safe to delete when copying -->
<div class="a14y-doc">
<h1>Sneakers Text Decode</h1>
<p>Characters scramble through random glyphs then snap left-to-right into readable text — like a cipher decode from the movie Sneakers.</p>
<h2>How it works</h2>
<p>Extracted from Infisical's React component in module 49899 of pages/index-4fc63f88002b15b5.js (HAR: https://infisical.com/_next/static/chunks/pages/index-4fc63f88002b15b5.js). The component is registered as s.A on the index page and applied to section-break labels (font-v2-mono uppercase), button text (cipher:true, cipherOnMount:true), and product-tab headings.</p>
<p>The character pool is exactly 76 chars: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%&*?/<>+=-. The timing parameters (all extracted verbatim) are: stagger = 70 ms, revealPerChar = 220 ms (the initial hold before char 0 resolves), glyphInterval = 80 ms (how often each pending glyph swaps), and maxDuration = 1500 ms (a cap that compresses stagger for long strings via r = (1500 - 220 - 200) / len). An IntersectionObserver with threshold: 0.4 triggers the animation once the element enters the viewport.</p>
<p>The reveal algorithm runs in a single requestAnimationFrame loop. On every frame it rebuilds the element's text content character-by-character: if elapsed time s >= index × r + 220 the real character is shown; otherwise the stored glyph is emitted and, if s - lastSwap >= 80, a new random glyph is sampled and the swap timestamp is updated. Spaces bypass the scramble entirely. When s exceeds the total duration, textContent is snapped to the final real string.</p>
<p>This rebuild is all DOM text (manipulating textContent of <div> and <span> elements) in a monospace font so glyph-width churn does not shift surrounding layout. The demo orchestrates an eyebrow → headline → secondary → eight grid-label cascade with 120 ms inter-label stagger, looping through three content sets drawn from real Infisical copy.</p>
<h2>How to use this file</h2>
<p>This is a complete, self-contained HTML document — copy it verbatim rather than reconstructing it. Markdown version: <a href="https://fx.statico.io/effects/sneakers-text.md">https://fx.statico.io/effects/sneakers-text.md</a>. Full writeup and live preview: <a href="https://fx.statico.io/demos/sneakers-text">https://fx.statico.io/demos/sneakers-text</a>.</p>
<h2>Source and credit</h2>
<p>Effect originally seen on Infisical (https://infisical.com/) — credit to Infisical. This is an independent recreation built from scratch for educational use; no proprietary source code was copied. Released under the Unlicense.</p>
<h2>More</h2>
<p>Terms used above are defined in the <a href="https://fx.statico.io/glossary">glossary</a>. See also the <a href="https://fx.statico.io/sitemap.md">site map</a>, <a href="https://fx.statico.io/AGENTS.md">AGENTS.md</a> and <a href="https://fx.statico.io/llms.txt">llms.txt</a>.</p>
</div>
<!-- a14y:end body -->
<div class="panel">
<div class="eyebrow" id="eyebrow"></div>
<div class="headline" id="headline"></div>
<div class="secondary" id="secondary"></div>
<hr class="divider">
<div class="label-grid" id="label-grid"></div>
</div>
<script>
/* =====================================================================
Sneakers Text — Infisical cipher engine (DOM edition)
=====================================================================
Architecture
────────────
1. scrambleTo(el, text, onDone) — core animates el.textContent from
random glyphs → real text using exact Infisical timing math.
2. runCycle() — orchestrates the staggered reveal sequence of eyebrow →
headline → secondary → 8 label cells, then restarts after a pause.
3. Content arrays of real phrases extracted from infisical.com.
All animation is DOM text via requestAnimationFrame (no canvas).
===================================================================== */
(function () {
'use strict';
/* ── Exact character pool from Infisical module 49899 ─────────────── */
const CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%&*?/<>+=-';
/* ── Exact timing params (defaults from module 49899) ─────────────── */
const STAGGER = 70; // ms per-char stagger between reveals
const REVEAL_DELAY = 220; // ms before the FIRST character snaps into place
const GLYPH_INT = 80; // ms between random glyph swaps per unresolved char
const MAX_DUR = 1500; // ms total cap (stagger is compressed for long text)
/* ── Core scramble function ───────────────────────────────────────── */
// Animates el.textContent: all chars scramble simultaneously; each one
// snaps to its real value at (index × r + REVEAL_DELAY) ms.
// Returns a cancel handle (call it to abort mid-flight).
function scrambleTo(el, text, onDone) {
const len = text.length;
if (len === 0) { el.textContent = ''; if (onDone) onDone(); return () => {}; }
// Infisical's stagger-compression formula — keeps total ≤ MAX_DUR
const r = (len * STAGGER + REVEAL_DELAY + 200 > MAX_DUR && len > 0)
? Math.max(0, (MAX_DUR - REVEAL_DELAY - 200) / len)
: STAGGER;
const totalDur = len * r + REVEAL_DELAY + 200;
// Per-character scramble state
const charState = text.split('').map(() => ({
lastSwap: 0,
glyph: CHARS[Math.floor(Math.random() * CHARS.length)],
}));
const start = performance.now();
let raf = null;
let cancelled = false;
function tick(now) {
if (cancelled) return;
const s = now - start;
let out = '';
for (let i = 0; i < len; i++) {
const ch = text[i];
// Spaces always pass through instantly (Infisical behaviour)
if (ch === ' ') { out += ' '; continue; }
if (s >= i * r + REVEAL_DELAY) {
// Character has reached its reveal time — lock it
out += ch;
} else {
// Still scrambling: swap glyph every GLYPH_INT ms
const c = charState[i];
if (s - c.lastSwap >= GLYPH_INT) {
c.glyph = CHARS[Math.floor(Math.random() * CHARS.length)];
c.lastSwap = s;
}
out += c.glyph;
}
}
el.textContent = out;
if (s < totalDur) {
raf = requestAnimationFrame(tick);
} else {
// Snap to final real text (cleans up any floating-point timing edge cases)
el.textContent = text;
if (onDone) onDone();
}
}
raf = requestAnimationFrame(tick);
return function cancel() {
cancelled = true;
if (raf !== null) { cancelAnimationFrame(raf); raf = null; }
};
}
/* ── Phrase arrays (real content from infisical.com) ──────────────── */
const EYEBROW_PHRASES = [
'INFISICAL.COM / CIPHER EFFECT',
'SECURITY PLATFORM / DECODE SEQUENCE',
'INFISICAL.COM / SECRETS ENGINE',
'SECURITY PLATFORM / CERTIFICATE MGT',
];
const HEADLINE_PHRASES = [
'Secrets Management',
'Certificate Management',
'Privileged Access Management',
'Secret Rotation + Dynamic Secrets',
'Granular Access Controls',
'AI Agent Security',
];
const SECONDARY_PHRASES = [
'One source of truth for every application secret.',
'Run your private PKI on autopilot.',
'Just-in-time entry to your most critical systems.',
'Rotate credentials on a schedule and issue short-lived secrets.',
'Scope access to secrets by environment, path, and identity.',
'Brokered access eliminates credential exfiltration risk.',
];
// 8 grid labels per cycle (3 complete sets, cycling)
const LABEL_SETS = [
[
'Granular Access Controls', 'Secret Rotation',
'Approval Workflows', 'Dynamic Secrets',
'PKI Dashboard', 'Certificate Discovery',
'Session Recording', 'Credential Rotation',
],
[
'Certificate Sync', 'OIDC Authentication',
'Audit Logs', 'Enterprise SSO',
'Secret Versioning', 'Access Policies',
'Integrations', 'Enterprise Ready',
],
[
'AWS Integration', 'GitHub Actions',
'Kubernetes Secrets', 'Docker Compose',
'Terraform Provider', 'GitLab CI',
'Azure Key Vault', 'Google Cloud',
],
];
/* ── Build the label grid DOM ─────────────────────────────────────── */
const gridEl = document.getElementById('label-grid');
const labelEls = [];
const dotEls = [];
for (let i = 0; i < 8; i++) {
const cell = document.createElement('div');
cell.className = 'label-cell';
const dot = document.createElement('div');
dot.className = 'dot';
const txt = document.createElement('div');
txt.className = 'label-text';
cell.appendChild(dot);
cell.appendChild(txt);
gridEl.appendChild(cell);
labelEls.push(txt);
dotEls.push(dot);
}
/* ── Element references ───────────────────────────────────────────── */
const eyebrowEl = document.getElementById('eyebrow');
const headlineEl = document.getElementById('headline');
const secEl = document.getElementById('secondary');
/* ── Reduced-motion path ─────────────────────────────────────────── */
// If user prefers reduced motion, just rotate text without scrambling
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
let idx = 0;
function showStatic() {
const s = LABEL_SETS[idx % LABEL_SETS.length];
eyebrowEl.textContent = EYEBROW_PHRASES[idx % EYEBROW_PHRASES.length];
headlineEl.textContent = HEADLINE_PHRASES[idx % HEADLINE_PHRASES.length];
secEl.textContent = SECONDARY_PHRASES[idx % SECONDARY_PHRASES.length];
labelEls.forEach((el, i) => { el.textContent = (s[i] || '').toUpperCase(); });
idx++;
setTimeout(showStatic, 3000);
}
showStatic();
return; // skip the animation path below
}
/* ── Orchestration loop ───────────────────────────────────────────── */
let cycle = 0;
let cancellers = []; // cancel handles for the current cycle
// Duration of the label stagger sequence (8 labels × 120ms apart + 1500ms last anim)
const LABEL_STAGGER_SPAN = 7 * 120 + MAX_DUR; // ≈ 2340ms
const LABEL_START_MS = 600; // when first label starts relative to cycle start
const PAUSE_MS = 1800; // idle hold after everything has settled
function cancelCycle() {
cancellers.forEach(c => typeof c === 'function' && c());
cancellers = [];
dotEls.forEach(d => d.classList.remove('active'));
}
function runCycle() {
cancelCycle();
const labels = LABEL_SETS[cycle % LABEL_SETS.length];
// 1. Eyebrow — immediate
cancellers.push(
scrambleTo(eyebrowEl, EYEBROW_PHRASES[cycle % EYEBROW_PHRASES.length])
);
// 2. Headline — starts 200 ms in
const t1 = setTimeout(() => {
cancellers.push(
scrambleTo(headlineEl, HEADLINE_PHRASES[cycle % HEADLINE_PHRASES.length])
);
}, 200);
cancellers.push(() => clearTimeout(t1));
// 3. Secondary — starts 400 ms in
const t2 = setTimeout(() => {
cancellers.push(
scrambleTo(secEl, SECONDARY_PHRASES[cycle % SECONDARY_PHRASES.length])
);
}, 400);
cancellers.push(() => clearTimeout(t2));
// 4. Label cells — each staggered 120 ms apart, first starts at 600 ms
for (let i = 0; i < 8; i++) {
(function (idx) {
const tLabel = setTimeout(() => {
dotEls[idx].classList.add('active');
cancellers.push(
scrambleTo(labelEls[idx], (labels[idx] || '').toUpperCase(), () => {
// Dot dims after the text has settled
setTimeout(() => dotEls[idx].classList.remove('active'), 600);
})
);
}, LABEL_START_MS + idx * 120);
cancellers.push(() => clearTimeout(tLabel));
})(i);
}
// 5. Schedule next cycle
const totalCycleDur = LABEL_START_MS + LABEL_STAGGER_SPAN + PAUSE_MS;
const tNext = setTimeout(() => {
cycle++;
runCycle();
}, totalCycleDur);
cancellers.push(() => clearTimeout(tNext));
}
runCycle();
})();
</script>
</body>
</html>
Source & credit
Effect seen on https://infisical.com/ — credit to Infisical.
This is an independent recreation of the effect built from scratch for educational use. No proprietary source code was copied.
<!doctype html>
<!--
Sneakers Text — Infisical cipher-scramble DOM rebuild
─────────────────────────────────────────────────────────────────────────────
Faithful port of Infisical's character-scramble effect (module 49899 in
https://infisical.com/_next/static/chunks/pages/index-4fc63f88002b15b5.js).
Every text node is a live DOM element whose textContent is updated
character-by-character on each requestAnimationFrame — no <canvas>.
Characters stay the same width because the font is monospace throughout.
Extracted parameters (exact, from module 49899):
• Character pool: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
0123456789!@#$%&*?/<>+=- (76 chars total)
• stagger = 70 ms (per-char reveal delay; compressed if text is long)
• revealPerChar = 220 ms (initial hold before first char locks in)
• glyphInterval = 80 ms (how often each unresolved glyph swaps)
• maxDuration = 1500 ms (total animation cap; stagger is recalculated)
• IntersectionObserver threshold = 0.4 (we trigger on load instead)
Completely standalone — no CDN, no web fonts, no external assets.
-->
<html lang="en">
<head>
<link rel="icon" href="data:,">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sneakers Text Decode</title>
<!-- a14y:begin head — machine-readable metadata; safe to delete when copying -->
<meta name="description" content="Characters scramble through random glyphs then snap left-to-right into readable text — like a cipher decode from the movie Sneakers.">
<link rel="canonical" href="https://fx.statico.io/effects/sneakers-text.html">
<link rel="alternate" type="text/markdown" href="https://fx.statico.io/effects/sneakers-text.md" title="Sneakers Text Decode (Markdown)">
<meta property="og:type" content="website">
<meta property="og:site_name" content="cool web fx">
<meta property="og:title" content="Sneakers Text Decode">
<meta property="og:description" content="Characters scramble through random glyphs then snap left-to-right into readable text — like a cipher decode from the movie Sneakers.">
<meta property="og:url" content="https://fx.statico.io/effects/sneakers-text.html">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Sneakers Text Decode">
<meta name="twitter:description" content="Characters scramble through random glyphs then snap left-to-right into readable text — like a cipher decode from the movie Sneakers.">
<script type="application/ld+json">{"@context":"https://schema.org","@graph":[{"@type":"SoftwareSourceCode","@id":"https://fx.statico.io/effects/sneakers-text.html#effect","name":"Sneakers Text Decode","url":"https://fx.statico.io/effects/sneakers-text.html","abstract":"Characters scramble through random glyphs then snap left-to-right into readable text — like a cipher decode from the movie Sneakers.","description":"Characters scramble through random glyphs then snap left-to-right into readable text — like a cipher decode from the movie Sneakers.","programmingLanguage":"HTML","codeSampleType":"full solution","runtimePlatform":"Web browser","codeRepository":"https://github.com/statico/cool-web-fx","license":"https://unlicense.org/","isBasedOn":"https://infisical.com/","mainEntityOfPage":"https://fx.statico.io/demos/sneakers-text","dateModified":"2026-07-27T17:24:00-07:00","inLanguage":"en"},{"@type":"BreadcrumbList","@id":"https://fx.statico.io/effects/sneakers-text.html#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://fx.statico.io/"},{"@type":"ListItem","position":2,"name":"Sneakers Text Decode","item":"https://fx.statico.io/demos/sneakers-text"},{"@type":"ListItem","position":3,"name":"Standalone source","item":"https://fx.statico.io/effects/sneakers-text.html"}]}]}</script>
<style>.a14y-doc{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}</style>
<!-- a14y:end head -->
<style>
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
html, body {
width: 100%;
height: 100%;
overflow: hidden;
background: #080a0e;
display: flex;
align-items: center;
justify-content: center;
font-family: 'Consolas', 'SF Mono', 'Monaco', 'Courier New', monospace;
-webkit-font-smoothing: antialiased;
}
/* ── Outer container ──────────────────────────────────────────────── */
.panel {
width: min(720px, 88vw);
display: flex;
flex-direction: column;
gap: 0;
user-select: none;
}
/* ── Eyebrow tag ──────────────────────────────────────────────────── */
.eyebrow {
font-size: 10px;
letter-spacing: 0.14em;
color: #a3e635;
opacity: 0.72;
margin-bottom: 28px;
min-height: 13px;
text-transform: uppercase;
}
/* ── Hero headline (cycles through phrases) ───────────────────────── */
.headline {
font-size: clamp(26px, 3.8vw, 46px);
font-weight: 600;
color: #edf2f7;
letter-spacing: -0.025em;
line-height: 1.1;
min-height: 1.15em;
}
/* ── Secondary line ───────────────────────────────────────────────── */
.secondary {
margin-top: 18px;
font-size: 12px;
color: #4a5568;
letter-spacing: 0.04em;
min-height: 16px;
}
/* ── Divider ──────────────────────────────────────────────────────── */
.divider {
margin-top: 36px;
border: none;
border-top: 1px solid rgba(255,255,255,0.06);
}
/* ── Label grid (2 columns, 4 rows) ───────────────────────────────── */
.label-grid {
margin-top: 0;
display: grid;
grid-template-columns: 1fr 1fr;
}
.label-cell {
display: flex;
align-items: center;
gap: 10px;
padding: 13px 0;
border-bottom: 1px solid rgba(255,255,255,0.06);
}
/* Right column cells get left padding and a vertical separator */
.label-cell:nth-child(even) {
padding-left: 28px;
border-left: 1px solid rgba(255,255,255,0.06);
}
.label-cell:nth-child(odd) {
padding-right: 28px;
}
/* Dot indicator — visible only when that cell's text is decoding */
.dot {
flex-shrink: 0;
width: 4px;
height: 4px;
border-radius: 50%;
background: #a3e635;
opacity: 0;
transition: opacity 200ms ease;
}
.dot.active { opacity: 0.55; }
.label-text {
font-size: 10px;
letter-spacing: 0.07em;
color: #718096;
text-transform: uppercase;
min-height: 13px;
/* Prevent layout reflow as random glyphs swap in/out */
white-space: nowrap;
overflow: hidden;
}
/* ── Reduced-motion: skip scramble, show plain text ─────────────── */
@media (prefers-reduced-motion: reduce) {
.eyebrow, .headline, .secondary, .label-text {
/* Content will be set by JS but animation won't run */
}
.dot { display: none; }
}
</style>
</head>
<body>
<!-- a14y:begin body — visually hidden description; safe to delete when copying -->
<div class="a14y-doc">
<h1>Sneakers Text Decode</h1>
<p>Characters scramble through random glyphs then snap left-to-right into readable text — like a cipher decode from the movie Sneakers.</p>
<h2>How it works</h2>
<p>Extracted from Infisical's React component in module 49899 of pages/index-4fc63f88002b15b5.js (HAR: https://infisical.com/_next/static/chunks/pages/index-4fc63f88002b15b5.js). The component is registered as s.A on the index page and applied to section-break labels (font-v2-mono uppercase), button text (cipher:true, cipherOnMount:true), and product-tab headings.</p>
<p>The character pool is exactly 76 chars: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%&*?/<>+=-. The timing parameters (all extracted verbatim) are: stagger = 70 ms, revealPerChar = 220 ms (the initial hold before char 0 resolves), glyphInterval = 80 ms (how often each pending glyph swaps), and maxDuration = 1500 ms (a cap that compresses stagger for long strings via r = (1500 - 220 - 200) / len). An IntersectionObserver with threshold: 0.4 triggers the animation once the element enters the viewport.</p>
<p>The reveal algorithm runs in a single requestAnimationFrame loop. On every frame it rebuilds the element's text content character-by-character: if elapsed time s >= index × r + 220 the real character is shown; otherwise the stored glyph is emitted and, if s - lastSwap >= 80, a new random glyph is sampled and the swap timestamp is updated. Spaces bypass the scramble entirely. When s exceeds the total duration, textContent is snapped to the final real string.</p>
<p>This rebuild is all DOM text (manipulating textContent of <div> and <span> elements) in a monospace font so glyph-width churn does not shift surrounding layout. The demo orchestrates an eyebrow → headline → secondary → eight grid-label cascade with 120 ms inter-label stagger, looping through three content sets drawn from real Infisical copy.</p>
<h2>How to use this file</h2>
<p>This is a complete, self-contained HTML document — copy it verbatim rather than reconstructing it. Markdown version: <a href="https://fx.statico.io/effects/sneakers-text.md">https://fx.statico.io/effects/sneakers-text.md</a>. Full writeup and live preview: <a href="https://fx.statico.io/demos/sneakers-text">https://fx.statico.io/demos/sneakers-text</a>.</p>
<h2>Source and credit</h2>
<p>Effect originally seen on Infisical (https://infisical.com/) — credit to Infisical. This is an independent recreation built from scratch for educational use; no proprietary source code was copied. Released under the Unlicense.</p>
<h2>More</h2>
<p>Terms used above are defined in the <a href="https://fx.statico.io/glossary">glossary</a>. See also the <a href="https://fx.statico.io/sitemap.md">site map</a>, <a href="https://fx.statico.io/AGENTS.md">AGENTS.md</a> and <a href="https://fx.statico.io/llms.txt">llms.txt</a>.</p>
</div>
<!-- a14y:end body -->
<div class="panel">
<div class="eyebrow" id="eyebrow"></div>
<div class="headline" id="headline"></div>
<div class="secondary" id="secondary"></div>
<hr class="divider">
<div class="label-grid" id="label-grid"></div>
</div>
<script>
/* =====================================================================
Sneakers Text — Infisical cipher engine (DOM edition)
=====================================================================
Architecture
────────────
1. scrambleTo(el, text, onDone) — core animates el.textContent from
random glyphs → real text using exact Infisical timing math.
2. runCycle() — orchestrates the staggered reveal sequence of eyebrow →
headline → secondary → 8 label cells, then restarts after a pause.
3. Content arrays of real phrases extracted from infisical.com.
All animation is DOM text via requestAnimationFrame (no canvas).
===================================================================== */
(function () {
'use strict';
/* ── Exact character pool from Infisical module 49899 ─────────────── */
const CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%&*?/<>+=-';
/* ── Exact timing params (defaults from module 49899) ─────────────── */
const STAGGER = 70; // ms per-char stagger between reveals
const REVEAL_DELAY = 220; // ms before the FIRST character snaps into place
const GLYPH_INT = 80; // ms between random glyph swaps per unresolved char
const MAX_DUR = 1500; // ms total cap (stagger is compressed for long text)
/* ── Core scramble function ───────────────────────────────────────── */
// Animates el.textContent: all chars scramble simultaneously; each one
// snaps to its real value at (index × r + REVEAL_DELAY) ms.
// Returns a cancel handle (call it to abort mid-flight).
function scrambleTo(el, text, onDone) {
const len = text.length;
if (len === 0) { el.textContent = ''; if (onDone) onDone(); return () => {}; }
// Infisical's stagger-compression formula — keeps total ≤ MAX_DUR
const r = (len * STAGGER + REVEAL_DELAY + 200 > MAX_DUR && len > 0)
? Math.max(0, (MAX_DUR - REVEAL_DELAY - 200) / len)
: STAGGER;
const totalDur = len * r + REVEAL_DELAY + 200;
// Per-character scramble state
const charState = text.split('').map(() => ({
lastSwap: 0,
glyph: CHARS[Math.floor(Math.random() * CHARS.length)],
}));
const start = performance.now();
let raf = null;
let cancelled = false;
function tick(now) {
if (cancelled) return;
const s = now - start;
let out = '';
for (let i = 0; i < len; i++) {
const ch = text[i];
// Spaces always pass through instantly (Infisical behaviour)
if (ch === ' ') { out += ' '; continue; }
if (s >= i * r + REVEAL_DELAY) {
// Character has reached its reveal time — lock it
out += ch;
} else {
// Still scrambling: swap glyph every GLYPH_INT ms
const c = charState[i];
if (s - c.lastSwap >= GLYPH_INT) {
c.glyph = CHARS[Math.floor(Math.random() * CHARS.length)];
c.lastSwap = s;
}
out += c.glyph;
}
}
el.textContent = out;
if (s < totalDur) {
raf = requestAnimationFrame(tick);
} else {
// Snap to final real text (cleans up any floating-point timing edge cases)
el.textContent = text;
if (onDone) onDone();
}
}
raf = requestAnimationFrame(tick);
return function cancel() {
cancelled = true;
if (raf !== null) { cancelAnimationFrame(raf); raf = null; }
};
}
/* ── Phrase arrays (real content from infisical.com) ──────────────── */
const EYEBROW_PHRASES = [
'INFISICAL.COM / CIPHER EFFECT',
'SECURITY PLATFORM / DECODE SEQUENCE',
'INFISICAL.COM / SECRETS ENGINE',
'SECURITY PLATFORM / CERTIFICATE MGT',
];
const HEADLINE_PHRASES = [
'Secrets Management',
'Certificate Management',
'Privileged Access Management',
'Secret Rotation + Dynamic Secrets',
'Granular Access Controls',
'AI Agent Security',
];
const SECONDARY_PHRASES = [
'One source of truth for every application secret.',
'Run your private PKI on autopilot.',
'Just-in-time entry to your most critical systems.',
'Rotate credentials on a schedule and issue short-lived secrets.',
'Scope access to secrets by environment, path, and identity.',
'Brokered access eliminates credential exfiltration risk.',
];
// 8 grid labels per cycle (3 complete sets, cycling)
const LABEL_SETS = [
[
'Granular Access Controls', 'Secret Rotation',
'Approval Workflows', 'Dynamic Secrets',
'PKI Dashboard', 'Certificate Discovery',
'Session Recording', 'Credential Rotation',
],
[
'Certificate Sync', 'OIDC Authentication',
'Audit Logs', 'Enterprise SSO',
'Secret Versioning', 'Access Policies',
'Integrations', 'Enterprise Ready',
],
[
'AWS Integration', 'GitHub Actions',
'Kubernetes Secrets', 'Docker Compose',
'Terraform Provider', 'GitLab CI',
'Azure Key Vault', 'Google Cloud',
],
];
/* ── Build the label grid DOM ─────────────────────────────────────── */
const gridEl = document.getElementById('label-grid');
const labelEls = [];
const dotEls = [];
for (let i = 0; i < 8; i++) {
const cell = document.createElement('div');
cell.className = 'label-cell';
const dot = document.createElement('div');
dot.className = 'dot';
const txt = document.createElement('div');
txt.className = 'label-text';
cell.appendChild(dot);
cell.appendChild(txt);
gridEl.appendChild(cell);
labelEls.push(txt);
dotEls.push(dot);
}
/* ── Element references ───────────────────────────────────────────── */
const eyebrowEl = document.getElementById('eyebrow');
const headlineEl = document.getElementById('headline');
const secEl = document.getElementById('secondary');
/* ── Reduced-motion path ─────────────────────────────────────────── */
// If user prefers reduced motion, just rotate text without scrambling
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
let idx = 0;
function showStatic() {
const s = LABEL_SETS[idx % LABEL_SETS.length];
eyebrowEl.textContent = EYEBROW_PHRASES[idx % EYEBROW_PHRASES.length];
headlineEl.textContent = HEADLINE_PHRASES[idx % HEADLINE_PHRASES.length];
secEl.textContent = SECONDARY_PHRASES[idx % SECONDARY_PHRASES.length];
labelEls.forEach((el, i) => { el.textContent = (s[i] || '').toUpperCase(); });
idx++;
setTimeout(showStatic, 3000);
}
showStatic();
return; // skip the animation path below
}
/* ── Orchestration loop ───────────────────────────────────────────── */
let cycle = 0;
let cancellers = []; // cancel handles for the current cycle
// Duration of the label stagger sequence (8 labels × 120ms apart + 1500ms last anim)
const LABEL_STAGGER_SPAN = 7 * 120 + MAX_DUR; // ≈ 2340ms
const LABEL_START_MS = 600; // when first label starts relative to cycle start
const PAUSE_MS = 1800; // idle hold after everything has settled
function cancelCycle() {
cancellers.forEach(c => typeof c === 'function' && c());
cancellers = [];
dotEls.forEach(d => d.classList.remove('active'));
}
function runCycle() {
cancelCycle();
const labels = LABEL_SETS[cycle % LABEL_SETS.length];
// 1. Eyebrow — immediate
cancellers.push(
scrambleTo(eyebrowEl, EYEBROW_PHRASES[cycle % EYEBROW_PHRASES.length])
);
// 2. Headline — starts 200 ms in
const t1 = setTimeout(() => {
cancellers.push(
scrambleTo(headlineEl, HEADLINE_PHRASES[cycle % HEADLINE_PHRASES.length])
);
}, 200);
cancellers.push(() => clearTimeout(t1));
// 3. Secondary — starts 400 ms in
const t2 = setTimeout(() => {
cancellers.push(
scrambleTo(secEl, SECONDARY_PHRASES[cycle % SECONDARY_PHRASES.length])
);
}, 400);
cancellers.push(() => clearTimeout(t2));
// 4. Label cells — each staggered 120 ms apart, first starts at 600 ms
for (let i = 0; i < 8; i++) {
(function (idx) {
const tLabel = setTimeout(() => {
dotEls[idx].classList.add('active');
cancellers.push(
scrambleTo(labelEls[idx], (labels[idx] || '').toUpperCase(), () => {
// Dot dims after the text has settled
setTimeout(() => dotEls[idx].classList.remove('active'), 600);
})
);
}, LABEL_START_MS + idx * 120);
cancellers.push(() => clearTimeout(tLabel));
})(i);
}
// 5. Schedule next cycle
const totalCycleDur = LABEL_START_MS + LABEL_STAGGER_SPAN + PAUSE_MS;
const tNext = setTimeout(() => {
cycle++;
runCycle();
}, totalCycleDur);
cancellers.push(() => clearTimeout(tNext));
}
runCycle();
})();
</script>
</body>
</html>