A digital longread on the life of Bhakti Tirtha Swami, after Black Lotus. Ten chapters, a cinematic video hero, interactive sacred objects in CSS/SVG, a timeline that unfolds on scroll. Dark palette with gold, three typefaces, zero frameworks.
A biographical longread based on a chapter from Steven Rosen's Black Lotus about Bhakti Tirtha Swami — the first person of African descent to become an initiating guru in the Vaishnava tradition. The task was to turn a book chapter into a standalone web experience: one that people read to the end rather than skim, and whose form doesn't argue with its content — a spiritual biography, not a promotional page.

The text is split into ten chapters, each a complete episode: childhood in Cleveland, Princeton and disillusionment with success, meeting Prabhupada, years behind the Iron Curtain, the mission in West Africa, twenty books in twenty languages, death as the final service.
"Instead of a uniform feed — a changing rhythm: dense text gives way to a full-screen quote, the quote to a video frame, the video to a timeline."

A dark palette — deep brown-black ground, warm gold, accents of saffron and lotus pink — nods to temple aesthetics without becoming a pastiche. Typography on three contrasting faces: Playfair Display for headings, Crimson Text for quotes and body text, Inter for captions and dates.

The hero is a looped video at 0.35× speed under a radial veil, with the title fading in step by step. The loop seam is hidden by an opacity cross-fade at the clip's edges — no jump. Three more video scenes are placed between chapters as pauses for meaning; in the climactic chapter a quote and a video sit side by side in a split layout.
const hv = document.getElementById('hero-video');
hv.addEventListener('loadedmetadata', () => { hv.playbackRate = 0.35; });
// seamless loop: fade out over the last second, fade in over the first
hv.addEventListener('timeupdate', () => {
const rem = hv.duration - hv.currentTime;
if (rem < 1) hv.style.opacity = Math.max(0, rem);
else if (hv.currentTime < 1) hv.style.opacity = Math.min(1, hv.currentTime);
else hv.style.opacity = 1;
});

Between chapters — four objects drawn in CSS and SVG: Om, a blooming lotus, a spinning Sudarshana chakra and a yantra of six triangles. They respond not to hover but to cursor proximity: within a 400 px radius a proximity factor is computed, and by it the petals open, the Om rings "breathe", the chakra spins faster, the yantra pulses. On touch devices — on tap.
They aren't decoration: each stands where it matters to the story — the lotus after the Princeton crisis, the chakra before the West Africa chapter.
function getSacredState(el, px, py) {
const r = el.getBoundingClientRect();
const dist = Math.hypot(px - (r.left + r.width / 2), py - (r.top + r.height / 2));
return { prox: Math.max(0, 1 - dist / 400) }; // 0 — far, 1 — at the centre
}
function updateBloom(el, prox) {
el.querySelectorAll('.bloom-petal').forEach((p, i) => {
const rad = (i * 45 - 90) * Math.PI / 180, spread = prox * 14;
p.style.transform = `translate(${Math.cos(rad) * spread}px, ${Math.sin(rad) * spread}px)`;
p.style.opacity = 0.12 + prox * 0.55;
});
}
// chakra: three rings rotate in opposite directions at different speeds
(function spinChakra() {
chakraAngle += chakraSpeed;
co.setAttribute('transform', `rotate(${chakraAngle},140,140)`);
cm.setAttribute('transform', `rotate(${-chakraAngle * 0.6},140,140)`);
ci.setAttribute('transform', `rotate(${chakraAngle * 1.4},140,140)`);
requestAnimationFrame(spinChakra);
})();


Blocks appear on scroll via IntersectionObserver; the eleven-event timeline unfolds as you move down. All animations are soft and long, with no abrupt motion: the page should slow you down, not hurry you.


A longread that reads like a book and looks like a film.