scroll down

FOUNDED BY DAMON ALBARN IN 1999, STUDIO 13 IN WEST LONDON IS AN INNOVATIVE STUDIO COMPLEX MARRYING CREATIVITY WITH AESTHETICS.

FOUNDED BY DAMON ALBARN IN 1999, STUDIO 13 IN WEST LONDON IS AN INNOVATIVE STUDIO COMPLEX MARRYING CREATIVITY WITH AESTHETICS.

Equipment availability may vary; please inquire for specific needs or reserve ahead of time.

OUTBOARD
MICROPHONES
KEYBOARDS
GUITARS & BASSES
AMPLIFIERS
PEDALS
DRUMS
PERCUSSIONS
OTHER INSTRUMENTS
TOYS
PLUG-INS

GR x IN
Designed In London

FOR BOOKINGS CONTACT INFO@13LTD.COM WITH THE SUBJECT 'STUDIO 13 HIRE'

FOR BOOKINGS CONTACT INFO@13LTD.COM WITH THE SUBJECT 'STUDIO 13 HIRE'

const isMobile = () => /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); const isElementInView = (el, margin = 0) => { if (!el) return false; const r = el.getBoundingClientRect(); const top = r.top - margin; const bottom = r.bottom + margin; // expand view window slightly return top < window.innerHeight && bottom > 0; }; const prefersReduced = typeof window !== "undefined" && window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches; if (!isMobile() && !prefersReduced) { // ---- State ---- let programmaticScroll = false; // true when JS is scrolling (animation/snap) let animating = false; // inertial animation active let v = 0; // velocity let lastY = window.pageYOffset || document.documentElement.scrollTop; let scrollTimeout; let abortSnap = false; // ---- Elements ---- const equipSection = document.querySelector('#equip'); // Build snap list: #snap1 .. #snap11 (skips missing IDs safely) const snapElements = Array.from({ length: 11 }, (_, i) => document.getElementById(snap${i+1})) .filter(Boolean); if (snapElements.length) { // ---- Tunables ---- const sensitivity = 0.12; // scroll sensitivity to wheel delta const damping = 0.92; // velocity decay per frame (slightly longer glide) const minV = 0.25; // stop threshold let snapThreshold = window.innerHeight * 0.35; // max distance to snap target // Keep snap window sensible on resize window.addEventListener('resize', () => { snapThreshold = window.innerHeight * 0.35; }); // ---- Inertial animation ---- function animate() { if (Math.abs(v) < minV) { animating = false; v = 0; programmaticScroll = false; // end of inertial control snapToClosest(); return; } programmaticScroll = true; v *= damping; window.scrollBy(0, v); lastY = window.pageYOffset || document.documentElement.scrollTop; requestAnimationFrame(animate); } // ---- Snapping ---- function snapToClosest() { if (abortSnap) return; const scrollY = window.pageYOffset || document.documentElement.scrollTop; let closest = null; let minDist = Infinity; snapElements.forEach(el => { const top = el.getBoundingClientRect().top + scrollY; const dist = Math.abs(top - scrollY); if (dist < minDist) { minDist = dist; closest = el; } }); if (!closest || minDist > snapThreshold) return; programmaticScroll = true; abortSnap = false; closest.scrollIntoView({ behavior: 'smooth', block: 'start' }); // Watch until native smooth scroll settles, or user interrupts let settleCount = 0, prev = -1, tries = 0; const watcher = () => { if (abortSnap) { programmaticScroll = false; return; } const cur = window.pageYOffset || document.documentElement.scrollTop; if (cur === prev) settleCount += 1; else settleCount = 0; prev = cur; tries++; if (settleCount >= 5 || tries > 240) { // ~4s cap programmaticScroll = false; lastY = cur; } else { requestAnimationFrame(watcher); } }; requestAnimationFrame(watcher); } // ---- Wheel handling (main control path) ---- window.addEventListener('wheel', (e) => { // Let #equip use native scroll if visible if (isElementInView(equipSection, 32)) return; // Otherwise take control and do inertial + snapping. e.preventDefault(); // Normalize units: deltaMode 1 => lines, 0 => pixels const dy = e.deltaMode === 1 ? e.deltaY * 16 : e.deltaY; v += dy * sensitivity; if (!animating) { animating = true; requestAnimationFrame(animate); } // If user wheels during snap, abort it abortSnap = true; clearTimeout(scrollTimeout); scrollTimeout = setTimeout(() => { abortSnap = false; v = 0; animating = false; programmaticScroll = false; snapToClosest(); }, 120); }, { passive: false }); // ---- Keyboard cancels snap too (PgUp/PgDn/Space/Home/End etc.) window.addEventListener('keydown', () => { abortSnap = true; }, { passive: true }); // ---- Keep lastY in sync without freezing native scrolls ---- window.addEventListener('scroll', () => { if (!programmaticScroll) { lastY = window.pageYOffset || document.documentElement.scrollTop; } }, { passive: true }); } }