Vertical reel with beat-synced cuts and subtitle word-highlight.
<!doctype html>
<!-- Example: tiktok-9x16 — beat-synced cuts · subtitle word-highlight · gradient sweep -->
<html data-duration="12" data-aspect="9:16"><head><style>
:root {
--cream:#f6f5f1; --cream-2:#efece4; --ink:#0a0a0a; --mute:#6b6862;
--line:#e3dfd3; --signal:#ff3b1f; --signal-2:#ff6a4a; --frame:#ffb800;
--green:#1f8a5b; --blue:#2b66ff;
}
* { box-sizing: border-box; }
body { margin:0; }
html, body { height: 100%; }
body { background: linear-gradient(160deg, #240a40 0%, #6a1e54 30%, var(--signal) 65%, var(--frame) 100%);
background-size: 200% 200%;
color: white; font-family: ui-sans-serif, system-ui; overflow: hidden;
display: grid; grid-template-rows: auto 1fr auto; padding: 32px;
position: relative; will-change: background-position; }
.flash { position: absolute; inset: 0; background: white; opacity: 0; pointer-events: none;
mix-blend-mode: overlay; }
.top { display:flex; justify-content:space-between; font-family: ui-monospace, monospace;
font-size: 13px; letter-spacing: .22em; opacity: .95; }
.hero { display:grid; place-items:center; text-align:center; padding: 0 16px; z-index: 2; }
.hero h1 { font-size: 108px; font-weight: 800; letter-spacing: -0.04em; line-height: 1;
text-shadow: 0 6px 40px rgba(0,0,0,.35); margin: 0; }
.hero h1 em { color: var(--frame); font-style: italic; }
.subs { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px 14px;
margin-top: 26px; font-size: 30px; font-weight: 600;
font-family: ui-sans-serif, system-ui; }
.subs span { transition: color .12s, transform .12s; opacity: .55; }
.subs span.on { color: var(--frame); opacity: 1; transform: scale(1.08); }
.cap { display:inline-block; padding: 9px 16px; background: var(--ink); color: white;
border-radius: 12px; font-family: ui-monospace, monospace; font-size: 19px;
letter-spacing: .04em; margin-top: 24px; }
.bottom { display:flex; flex-direction: column; gap: 8px; z-index: 2; }
.handle { font-family: ui-monospace, monospace; font-size: 16px; opacity: .95; }
.track { height: 4px; background: rgba(255,255,255,.25); border-radius: 999px; overflow:hidden; }
.track > i { display:block; height:100%; background:white; width:0%; }
</style></head><body>
<div class="flash" id="fl"></div>
<div class="top"><span>● LIVE</span><span id="tc">00:00</span></div>
<div class="hero">
<div>
<h1>Make it<br/><em id="word">loud</em></h1>
<div class="subs" id="subs">
<span>Write</span><span>HTML.</span><span>Render</span><span>video.</span>
<span>Ship</span><span>the</span><span>diff.</span>
</div>
<div class="cap" id="cap">// hyperframes.dev</div>
</div>
</div>
<div class="bottom">
<div class="handle">@hyperframes</div>
<div class="track"><i id="prog"></i></div>
</div>
<script>
var word = document.getElementById('word');
var cap = document.getElementById('cap');
var prog = document.getElementById('prog');
var tc = document.getElementById('tc');
var fl = document.getElementById('fl');
var subs = document.getElementById('subs').querySelectorAll('span');
var WORDS = ['loud', 'loop', 'land', 'last'];
var CAPS = ['// composable', '// reproducible', '// shippable', '// hyperframes.dev'];
var BPM = 128;
var beat = 60 / BPM;
var t = 0;
var reduced = matchMedia('(prefers-reduced-motion: reduce)').matches;
function render() {
var tt = reduced ? 4 : t;
var idx = Math.min(WORDS.length - 1, Math.floor(tt / 2));
word.textContent = WORDS[idx];
cap.textContent = CAPS[idx];
prog.style.width = Math.min(100, (tt / 8) * 100) + '%';
var total = Math.floor(tt * 1000);
var s = Math.floor(total / 1000);
var ms = Math.floor((total % 1000) / 10);
tc.textContent = String(s).padStart(2,'0') + ':' + String(ms).padStart(2,'0');
// Gradient sweep tracks the beat.
var bx = (tt / beat) % 2;
document.body.style.backgroundPosition = (bx * 50).toFixed(0) + '% ' + (bx * 30).toFixed(0) + '%';
// Subtitle word highlight by beat.
var beatIdx = Math.floor(tt / (beat * 0.5)) % subs.length;
subs.forEach(function(el, i) {
el.classList.toggle('on', i === beatIdx);
});
// Cut flash at each beat onset.
var phase = (tt % beat) / beat;
fl.style.opacity = String(Math.max(0, 0.45 - phase * 1.4));
}
addEventListener('hf-seek', function(e) { t = e.detail.time; render(); });
render();
</script>
</body></html>