Ethene Labs is a venture studio focused on token advisory and token GTM.
Token Advisory
Token economics design, market maker strategy, and exchange listing strategy.
GTM
Supercharging growth for Web3 teams.
Agency
Distribution of high-engagement community campaigns through KOLs.
VALIDATORS
Participating in consensus of top Layer-1 blockchains.
Team
ETHENE LABS
ETHENE LABS
ETHENE LABS
ETHENE LABS
ETHENE LABS
ETHENE LABS
ETHENE LABS
ETHENE LABS
ETHENE LABS
ETHENE LABS
ETHENE LABS
ETHENE LABS
ETHENE LABS
ETHENE LABS
ETHENE LABS
Selected Clients
tWITTER
LINKEDLIN
Selected Clients
tWITTER
LINKEDLIN
(function () { const wrapper = document.querySelector('.hero-wrapper'); if (!wrapper) return; const video = wrapper.querySelector('.hero-video'); const lines = wrapper.querySelectorAll('.line'); const NUM_LINES = lines.length; const FADE_RATIO = 0.25; const LERP_SPEED = 0.12; let duration = 0; let targetTime = 0; let currentTime = 0; let ready = false; function initVideo() { duration = video.duration; targetTime = duration; currentTime = duration; video.currentTime = duration; ready = true; } video.addEventListener('loadedmetadata', initVideo); // Load the whole video into memory and play it from a blob URL. // This removes network latency from scroll-driven seeking — on a live // server, seeking via currentTime can stall waiting for byte-range // requests, causing jerky scrubbing even though it's smooth on // localhost. With a blob URL, every frame is already local. const remoteSrc = video.dataset.src; if (remoteSrc) { fetch(remoteSrc) .then((res) => res.blob()) .then((blob) => { video.src = URL.createObjectURL(blob); }) .catch(() => { video.src = remoteSrc; }); } // Cache layout measurements instead of reading them every animation // frame. getBoundingClientRect()/offsetHeight force a synchronous layout // recalculation; doing that every frame while also writing styles every // frame (the opacity/transform updates below) causes layout thrashing — // cheap on a tiny test page, but very expensive on a full WordPress page // with hundreds of elements, which is what causes scroll jank live. let total = 0; function measure() { const rect = wrapper.getBoundingClientRect(); const offsetTop = rect.top + window.scrollY; total = offsetTop + (wrapper.offsetHeight - window.innerHeight); } measure(); window.addEventListener('resize', measure); window.addEventListener('load', measure); function getProgress() { let p = total > 0 ? window.scrollY / total : 0; return Math.min(Math.max(p, 0), 1); } function updateText(progress) { const segment = 1 / NUM_LINES; lines.forEach((line, i) => { const start = i * segment; const end = start + segment; const fade = segment * FADE_RATIO; let opacity = 0; let shift = 12; if (progress >= start && progress <= end) { if (progress < start + fade) { const t = (progress - start) / fade; opacity = t; shift = 12 * (1 - t); } else if (progress > end - fade) { const t = (end - progress) / fade; opacity = t; shift = -12 * (1 - t); } else { opacity = 1; shift = 0; } } line.style.opacity = opacity; line.style.transform = `translate(-50%, calc(-50% + ${shift}px))`; }); } function loop() { if (ready) { const progress = getProgress(); targetTime = duration * (1 - progress); currentTime += (targetTime - currentTime) * LERP_SPEED; if (Math.abs(currentTime - video.currentTime) > 0.01) { video.currentTime = currentTime; } updateText(progress); } requestAnimationFrame(loop); } requestAnimationFrame(loop); })();