talas-group/07_CONTENUS_MARKETING/microphone-v2.html

1498 lines
44 KiB
HTML
Raw Normal View History

<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RĒVO — Engineered to Last</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,300;0,400;0,600;1,300;1,400&family=JetBrains+Mono:wght@300;400;500;700&family=Outfit:wght@200;300;400;500;600&display=swap');
:root {
--bg: #060606;
--bg-card: rgba(12,12,12,0.8);
--text: #e2ddd5;
--text-dim: #5c574f;
--text-mid: #8a847b;
--copper: #c4875c;
--copper-bright: #e8a872;
--copper-glow: rgba(196,135,92,0.12);
--gold: #d4a574;
--green: #5fa85f;
--green-bg: rgba(95,168,95,0.08);
--green-border: rgba(95,168,95,0.2);
--line: rgba(255,255,255,0.04);
--display: 'Cormorant Garamond', serif;
--body: 'Outfit', sans-serif;
--mono: 'JetBrains Mono', monospace;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
html { scrollbar-width: none; }
html::-webkit-scrollbar { display: none; }
body {
background: var(--bg);
color: var(--text);
font-family: var(--body);
overflow-x: hidden;
}
.scroll-container { height: 700vh; }
/* ── CANVAS ── */
#scene-container {
position: fixed;
inset: 0;
z-index: 1;
}
/* ── AMBIENT GRID ── */
.ambient-grid {
position: fixed;
inset: 0;
z-index: 2;
pointer-events: none;
opacity: 0.025;
background-image:
linear-gradient(var(--line) 1px, transparent 1px),
linear-gradient(90deg, var(--line) 1px, transparent 1px);
background-size: 60px 60px;
mask-image: radial-gradient(ellipse 70% 60% at 50% 50%, black 20%, transparent 70%);
}
/* ── GRAIN OVERLAY ── */
.grain {
position: fixed;
inset: -50%;
width: 200%; height: 200%;
z-index: 998;
pointer-events: none;
opacity: 0.018;
background: url("data:image/svg+xml,%3Csvg viewBox='0 0 512 512' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='5' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
background-size: 256px;
}
/* ── VIGNETTE ── */
.vignette {
position: fixed;
inset: 0;
z-index: 3;
pointer-events: none;
background: radial-gradient(ellipse 65% 65% at 55% 45%, transparent 0%, rgba(0,0,0,0.75) 100%);
}
/* ── NAVIGATION ── */
.nav {
position: fixed;
top: 0; left: 0; width: 100%;
z-index: 100;
padding: 32px 56px;
display: flex;
justify-content: space-between;
align-items: center;
transition: opacity 0.5s;
}
.nav-brand {
display: flex;
align-items: baseline;
gap: 12px;
}
.nav-logo {
font-family: var(--display);
font-size: 24px;
font-weight: 300;
letter-spacing: 8px;
color: var(--text);
text-transform: uppercase;
}
.nav-tag {
font-family: var(--mono);
font-size: 8px;
font-weight: 300;
letter-spacing: 4px;
color: var(--text-dim);
text-transform: uppercase;
border: 1px solid rgba(255,255,255,0.06);
padding: 3px 10px;
border-radius: 2px;
}
.nav-links {
display: flex;
gap: 40px;
list-style: none;
}
.nav-links a {
font-family: var(--mono);
font-size: 10px;
letter-spacing: 2.5px;
color: var(--text-dim);
text-decoration: none;
text-transform: uppercase;
transition: color 0.4s;
position: relative;
}
.nav-links a::after {
content: '';
position: absolute;
bottom: -4px; left: 0;
width: 0; height: 1px;
background: var(--copper);
transition: width 0.4s;
}
.nav-links a:hover { color: var(--text); }
.nav-links a:hover::after { width: 100%; }
/* ── HERO ── */
.hero-content {
position: fixed;
z-index: 20;
pointer-events: none;
left: 56px;
top: 50%;
transform: translateY(-50%);
transition: opacity 0.6s, transform 0.6s;
}
.hero-eyebrow {
font-family: var(--mono);
font-size: 9px;
font-weight: 400;
letter-spacing: 5px;
text-transform: uppercase;
color: var(--copper);
margin-bottom: 20px;
display: flex;
align-items: center;
gap: 14px;
}
.hero-eyebrow::before {
content: '';
width: 40px; height: 1px;
background: linear-gradient(90deg, var(--copper), transparent);
}
.hero-h1 {
font-family: var(--display);
font-size: clamp(42px, 5.5vw, 80px);
font-weight: 300;
line-height: 1.05;
letter-spacing: -0.5px;
color: var(--text);
max-width: 480px;
}
.hero-h1 em {
font-style: italic;
font-weight: 300;
color: var(--copper-bright);
}
.hero-sub {
margin-top: 28px;
font-family: var(--body);
font-size: 15px;
font-weight: 200;
line-height: 1.8;
color: var(--text-mid);
max-width: 360px;
letter-spacing: 0.3px;
}
.hero-badges {
margin-top: 32px;
display: flex;
gap: 12px;
}
.hero-badge {
font-family: var(--mono);
font-size: 9px;
font-weight: 400;
letter-spacing: 2px;
text-transform: uppercase;
color: var(--text-dim);
border: 1px solid rgba(255,255,255,0.06);
padding: 6px 14px;
border-radius: 3px;
}
/* ── SCROLL CTA ── */
.scroll-cta {
position: fixed;
bottom: 44px; left: 50%;
transform: translateX(-50%);
z-index: 20;
pointer-events: none;
display: flex;
flex-direction: column;
align-items: center;
gap: 12px;
transition: opacity 0.5s;
}
.scroll-cta-text {
font-family: var(--mono);
font-size: 8px;
font-weight: 300;
letter-spacing: 5px;
text-transform: uppercase;
color: var(--text-dim);
}
.scroll-cta-line {
width: 1px; height: 48px;
background: rgba(255,255,255,0.08);
position: relative;
overflow: hidden;
border-radius: 1px;
}
.scroll-cta-line::after {
content: '';
position: absolute; top: -100%; left: 0;
width: 100%; height: 50%;
background: linear-gradient(to bottom, transparent, var(--copper));
animation: dropLine 2.2s ease-in-out infinite;
}
@keyframes dropLine {
0%, 100% { top: -50%; opacity: 0; }
30% { opacity: 1; }
70% { opacity: 1; }
90% { top: 120%; opacity: 0; }
}
/* ── PART ANNOTATIONS ── */
.annotation {
position: fixed;
z-index: 20;
pointer-events: none;
opacity: 0;
transition: opacity 0.7s cubic-bezier(0.16,1,0.3,1), transform 0.7s cubic-bezier(0.16,1,0.3,1);
}
.annotation.from-left { transform: translateX(-40px); }
.annotation.from-right { transform: translateX(40px); }
.annotation.visible { opacity: 1; transform: translateX(0); }
.anno-header {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 10px;
}
.anno-num {
font-family: var(--mono);
font-size: 9px;
font-weight: 500;
color: var(--bg);
background: var(--copper);
width: 22px; height: 22px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 3px;
}
.anno-tag {
font-family: var(--mono);
font-size: 8px;
font-weight: 400;
letter-spacing: 4px;
text-transform: uppercase;
color: var(--copper);
}
.anno-line {
width: 48px; height: 1px;
background: linear-gradient(90deg, var(--copper), transparent);
margin-bottom: 10px;
}
.annotation.from-right .anno-line {
background: linear-gradient(270deg, var(--copper), transparent);
margin-left: auto;
}
.anno-name {
font-family: var(--display);
font-size: clamp(26px, 3vw, 42px);
font-weight: 300;
line-height: 1.15;
color: var(--text);
margin-bottom: 12px;
}
.anno-desc {
font-family: var(--body);
font-size: 13px;
font-weight: 300;
line-height: 1.85;
color: var(--text-mid);
max-width: 300px;
letter-spacing: 0.2px;
}
.anno-specs {
margin-top: 18px;
display: flex;
gap: 24px;
}
.anno-spec {
display: flex;
flex-direction: column;
gap: 3px;
}
.anno-spec-val {
font-family: var(--mono);
font-size: 20px;
font-weight: 500;
color: var(--copper-bright);
line-height: 1;
}
.anno-spec-key {
font-family: var(--mono);
font-size: 7px;
font-weight: 400;
letter-spacing: 3px;
text-transform: uppercase;
color: var(--text-dim);
}
.anno-repairability {
margin-top: 16px;
display: inline-flex;
align-items: center;
gap: 8px;
padding: 6px 14px;
background: var(--green-bg);
border: 1px solid var(--green-border);
border-radius: 4px;
}
.anno-repair-icon {
font-size: 11px;
}
.anno-repair-text {
font-family: var(--mono);
font-size: 8px;
font-weight: 500;
letter-spacing: 2px;
text-transform: uppercase;
color: var(--green);
}
/* ── PROGRESS RAIL ── */
.progress-rail {
position: fixed;
right: 36px;
top: 50%;
transform: translateY(-50%);
z-index: 50;
display: flex;
flex-direction: column;
align-items: center;
gap: 0;
}
.progress-rail-track {
width: 2px;
height: 140px;
background: rgba(255,255,255,0.04);
border-radius: 2px;
position: relative;
overflow: hidden;
}
.progress-rail-fill {
position: absolute;
top: 0; left: 0;
width: 100%;
height: 0%;
background: linear-gradient(to bottom, var(--copper), var(--copper-bright));
border-radius: 2px;
transition: height 0.15s;
box-shadow: 0 0 8px var(--copper-glow);
}
.progress-rail-labels {
position: absolute;
right: 14px; top: 0;
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.prl {
font-family: var(--mono);
font-size: 7px;
letter-spacing: 2px;
text-transform: uppercase;
color: var(--text-dim);
opacity: 0.3;
transition: opacity 0.3s, color 0.3s;
text-align: right;
white-space: nowrap;
}
.prl.active {
opacity: 1;
color: var(--copper);
}
/* ── REPAIR SCORE ── */
.repair-floating {
position: fixed;
z-index: 25;
bottom: 80px; left: 56px;
pointer-events: none;
opacity: 0;
transform: translateY(20px);
transition: opacity 0.6s, transform 0.6s;
}
.repair-floating.visible {
opacity: 1;
transform: translateY(0);
}
.rf-card {
background: var(--bg-card);
backdrop-filter: blur(24px);
border: 1px solid var(--green-border);
border-radius: 10px;
padding: 20px 28px;
display: flex;
align-items: center;
gap: 18px;
}
.rf-score-ring {
width: 56px; height: 56px;
position: relative;
}
.rf-score-ring svg {
width: 100%; height: 100%;
transform: rotate(-90deg);
}
.rf-score-ring circle {
fill: none;
stroke-width: 3;
}
.rf-ring-bg { stroke: rgba(255,255,255,0.05); }
.rf-ring-fill {
stroke: var(--green);
stroke-dasharray: 160;
stroke-dashoffset: 0;
stroke-linecap: round;
filter: drop-shadow(0 0 4px rgba(95,168,95,0.3));
}
.rf-score-num {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
font-family: var(--mono);
font-size: 14px;
font-weight: 700;
color: var(--green);
}
.rf-info {
display: flex;
flex-direction: column;
gap: 3px;
}
.rf-title {
font-family: var(--mono);
font-size: 8px;
font-weight: 500;
letter-spacing: 3px;
text-transform: uppercase;
color: var(--green);
}
.rf-desc {
font-family: var(--body);
font-size: 12px;
font-weight: 300;
color: var(--text-dim);
}
/* ── BUY SECTION ── */
.buy-section {
position: fixed;
bottom: 0; left: 0; width: 100%;
z-index: 30;
pointer-events: none;
opacity: 0;
transform: translateY(60px);
transition: opacity 0.8s cubic-bezier(0.16,1,0.3,1), transform 0.8s cubic-bezier(0.16,1,0.3,1);
display: flex;
justify-content: center;
padding: 0 56px 52px;
}
.buy-section.visible {
opacity: 1;
transform: translateY(0);
pointer-events: all;
}
.buy-card {
background: var(--bg-card);
backdrop-filter: blur(30px);
border: 1px solid rgba(196,135,92,0.12);
border-radius: 14px;
padding: 28px 44px;
display: flex;
align-items: center;
gap: 44px;
max-width: 720px;
width: 100%;
position: relative;
overflow: hidden;
}
.buy-card::before {
content: '';
position: absolute;
top: 0; left: 0;
width: 100%; height: 1px;
background: linear-gradient(90deg, transparent, var(--copper), transparent);
opacity: 0.4;
}
.buy-left {
flex: 1;
}
.buy-pretitle {
font-family: var(--mono);
font-size: 8px;
font-weight: 400;
letter-spacing: 4px;
text-transform: uppercase;
color: var(--text-dim);
margin-bottom: 6px;
}
.buy-name {
font-family: var(--display);
font-size: 26px;
font-weight: 300;
color: var(--text);
margin-bottom: 4px;
}
.buy-price {
font-family: var(--mono);
font-size: 32px;
font-weight: 700;
color: var(--copper-bright);
line-height: 1;
}
.buy-meta {
margin-top: 8px;
display: flex;
gap: 16px;
}
.buy-meta span {
font-family: var(--mono);
font-size: 8px;
letter-spacing: 2px;
text-transform: uppercase;
color: var(--text-dim);
}
.buy-btn {
font-family: var(--mono);
font-size: 10px;
font-weight: 700;
letter-spacing: 4px;
text-transform: uppercase;
color: var(--bg);
background: linear-gradient(135deg, var(--copper), var(--copper-bright));
border: none;
border-radius: 60px;
padding: 20px 44px;
cursor: pointer;
transition: transform 0.4s cubic-bezier(0.16,1,0.3,1), box-shadow 0.4s;
white-space: nowrap;
position: relative;
overflow: hidden;
}
.buy-btn::after {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(135deg, transparent 40%, rgba(255,255,255,0.2) 50%, transparent 60%);
transform: translateX(-100%);
transition: transform 0.6s;
}
.buy-btn:hover {
transform: scale(1.06);
box-shadow: 0 10px 40px rgba(196,135,92,0.25);
}
.buy-btn:hover::after {
transform: translateX(100%);
}
/* ── CONNECTOR LINES (SVG overlay) ── */
#connector-lines {
position: fixed;
inset: 0;
z-index: 15;
pointer-events: none;
}
.conn-line {
stroke: var(--copper);
stroke-width: 0.5;
fill: none;
opacity: 0;
stroke-dasharray: 300;
stroke-dashoffset: 300;
transition: opacity 0.5s, stroke-dashoffset 1.2s cubic-bezier(0.16,1,0.3,1);
}
.conn-line.visible {
opacity: 0.4;
stroke-dashoffset: 0;
}
.conn-dot {
fill: var(--copper);
opacity: 0;
transition: opacity 0.5s;
}
.conn-dot.visible { opacity: 0.7; }
/* ── FLOATING PARTICLES ── */
#particle-canvas {
position: fixed;
inset: 0;
z-index: 4;
pointer-events: none;
}
/* ── COUNTERS ANIMATION ── */
@keyframes fadeInSpec {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
</style>
</head>
<body>
<div class="grain"></div>
<div class="vignette"></div>
<div class="ambient-grid"></div>
<canvas id="particle-canvas"></canvas>
<!-- CONNECTOR LINES SVG -->
<svg id="connector-lines" xmlns="http://www.w3.org/2000/svg">
<line class="conn-line" id="cl-grille" x1="0" y1="0" x2="0" y2="0"/>
<circle class="conn-dot" id="cd-grille" r="3"/>
<line class="conn-line" id="cl-capsule" x1="0" y1="0" x2="0" y2="0"/>
<circle class="conn-dot" id="cd-capsule" r="3"/>
<line class="conn-line" id="cl-pcb" x1="0" y1="0" x2="0" y2="0"/>
<circle class="conn-dot" id="cd-pcb" r="3"/>
<line class="conn-line" id="cl-body" x1="0" y1="0" x2="0" y2="0"/>
<circle class="conn-dot" id="cd-body" r="3"/>
<line class="conn-line" id="cl-connector" x1="0" y1="0" x2="0" y2="0"/>
<circle class="conn-dot" id="cd-connector" r="3"/>
</svg>
<!-- NAV -->
<nav class="nav" id="nav">
<div class="nav-brand">
<div class="nav-logo">Rēvo</div>
<div class="nav-tag">audio</div>
</div>
<ul class="nav-links">
<li><a href="#">Manifeste</a></li>
<li><a href="#">Réparation</a></li>
<li><a href="#">Open Source</a></li>
<li><a href="#">Acheter</a></li>
</ul>
</nav>
<!-- 3D -->
<div id="scene-container"></div>
<!-- HERO -->
<div class="hero-content" id="hero-content">
<div class="hero-eyebrow">Microphone condensateur</div>
<h1 class="hero-h1">Conçu pour<br>être <em>réparé</em></h1>
<p class="hero-sub">Chaque pièce se démonte, se remplace, se recycle. Zéro colle. Zéro clip cassable. Zéro obsolescence.</p>
<div class="hero-badges">
<div class="hero-badge">Open-Hardware</div>
<div class="hero-badge">Garantie 10 ans</div>
<div class="hero-badge">Pièces à vie</div>
</div>
</div>
<!-- SCROLL CTA -->
<div class="scroll-cta" id="scroll-cta">
<div class="scroll-cta-line"></div>
<div class="scroll-cta-text">Découvrir l'intérieur</div>
</div>
<!-- ANNOTATIONS -->
<div class="annotation from-right" id="anno-grille" style="top: 18%; right: 56px; max-width: 340px;">
<div class="anno-header">
<div class="anno-num">01</div>
<div class="anno-tag">Grille acoustique</div>
</div>
<div class="anno-line"></div>
<div class="anno-name">Acier inoxydable<br>micro-perforé</div>
<div class="anno-desc">Fixation magnétique — retrait sans outil en 2 secondes. Résiste aux chocs, lavable à l'eau. Remplaçable à vie pour 12 €.</div>
<div class="anno-specs">
<div class="anno-spec"><span class="anno-spec-val">304L</span><span class="anno-spec-key">Inox</span></div>
<div class="anno-spec"><span class="anno-spec-val">0.8mm</span><span class="anno-spec-key">Perforations</span></div>
<div class="anno-spec"><span class="anno-spec-val">2s</span><span class="anno-spec-key">Démontage</span></div>
</div>
<div class="anno-repairability"><span class="anno-repair-icon"></span><span class="anno-repair-text">Sans outil</span></div>
</div>
<div class="annotation from-left" id="anno-capsule" style="top: 28%; left: 56px; max-width: 340px;">
<div class="anno-header">
<div class="anno-num">02</div>
<div class="anno-tag">Capsule condensateur</div>
</div>
<div class="anno-line"></div>
<div class="anno-name">Membrane<br>plaquée or 34mm</div>
<div class="anno-desc">Directivité cardioïde, connecteur enfichable standardisé. Remplacement en 30 secondes. Upgrade possible vers membrane omni.</div>
<div class="anno-specs">
<div class="anno-spec"><span class="anno-spec-val">34mm</span><span class="anno-spec-key">Diamètre</span></div>
<div class="anno-spec"><span class="anno-spec-val">20Hz</span><span class="anno-spec-key">Low cut</span></div>
<div class="anno-spec"><span class="anno-spec-val">20kHz</span><span class="anno-spec-key">High end</span></div>
</div>
<div class="anno-repairability"><span class="anno-repair-icon"></span><span class="anno-repair-text">Connecteur enfichable</span></div>
</div>
<div class="annotation from-right" id="anno-pcb" style="top: 38%; right: 56px; max-width: 360px;">
<div class="anno-header">
<div class="anno-num">03</div>
<div class="anno-tag">Électronique</div>
</div>
<div class="anno-line"></div>
<div class="anno-name">PCB modulaire<br>open-source</div>
<div class="anno-desc">Préampli classe A discret, composants traversants soudables à la main. Schémas, Gerber et BOM publiés sous CERN-OHL-S v2. Modifiable, auditable, réparable par quiconque sait tenir un fer.</div>
<div class="anno-specs">
<div class="anno-spec"><span class="anno-spec-val">7dB</span><span class="anno-spec-key">Sensibilité</span></div>
<div class="anno-spec"><span class="anno-spec-val">THT</span><span class="anno-spec-key">Composants</span></div>
<div class="anno-spec"><span class="anno-spec-val">OHL-S</span><span class="anno-spec-key">Licence</span></div>
</div>
<div class="anno-repairability"><span class="anno-repair-icon"></span><span class="anno-repair-text">Soudure manuelle</span></div>
</div>
<div class="annotation from-left" id="anno-body" style="top: 52%; left: 56px; max-width: 340px;">
<div class="anno-header">
<div class="anno-num">04</div>
<div class="anno-tag">Corps</div>
</div>
<div class="anno-line"></div>
<div class="anno-name">Fût aluminium<br>6061-T6 usiné CNC</div>
<div class="anno-desc">Anodisation noir mat, assemblage par 4 vis apparentes. Aucune colle, aucun clip. Conçu pour survivre à une décennie de tournées.</div>
<div class="anno-specs">
<div class="anno-spec"><span class="anno-spec-val">6061</span><span class="anno-spec-key">Alliage</span></div>
<div class="anno-spec"><span class="anno-spec-val">CNC</span><span class="anno-spec-key">Usinage</span></div>
<div class="anno-spec"><span class="anno-spec-val">4 vis</span><span class="anno-spec-key">Fixation</span></div>
</div>
<div class="anno-repairability"><span class="anno-repair-icon"></span><span class="anno-repair-text">Tournevis cruciforme</span></div>
</div>
<div class="annotation from-right" id="anno-connector" style="top: 64%; right: 56px; max-width: 340px;">
<div class="anno-header">
<div class="anno-num">05</div>
<div class="anno-tag">Connectique</div>
</div>
<div class="anno-line"></div>
<div class="anno-name">Embase XLR<br>Neutrik plaqué or</div>
<div class="anno-desc">Vissée au châssis, pas soudée. Remplacement sans fer à souder. Compatible 48V phantom standard.</div>
<div class="anno-specs">
<div class="anno-spec"><span class="anno-spec-val">XLR-3</span><span class="anno-spec-key">Standard</span></div>
<div class="anno-spec"><span class="anno-spec-val">Au</span><span class="anno-spec-key">Contacts</span></div>
<div class="anno-spec"><span class="anno-spec-val">48V</span><span class="anno-spec-key">Phantom</span></div>
</div>
<div class="anno-repairability"><span class="anno-repair-icon"></span><span class="anno-repair-text">Vissé, pas soudé</span></div>
</div>
<!-- REPAIR SCORE -->
<div class="repair-floating" id="repair-floating">
<div class="rf-card">
<div class="rf-score-ring">
<svg viewBox="0 0 56 56">
<circle class="rf-ring-bg" cx="28" cy="28" r="24"/>
<circle class="rf-ring-fill" cx="28" cy="28" r="24"/>
</svg>
<div class="rf-score-num">10</div>
</div>
<div class="rf-info">
<div class="rf-title">Indice de réparabilité</div>
<div class="rf-desc">Score maximal — chaque pièce est<br>remplaçable individuellement</div>
</div>
</div>
</div>
<!-- PROGRESS RAIL -->
<div class="progress-rail" id="progress-rail">
<div class="progress-rail-track">
<div class="progress-rail-fill" id="prf"></div>
<div class="progress-rail-labels">
<span class="prl active" data-s="0">Intro</span>
<span class="prl" data-s="1">Grille</span>
<span class="prl" data-s="2">Capsule</span>
<span class="prl" data-s="3">PCB</span>
<span class="prl" data-s="4">Corps</span>
<span class="prl" data-s="5">XLR</span>
<span class="prl" data-s="6">Achat</span>
</div>
</div>
</div>
<!-- BUY -->
<div class="buy-section" id="buy-section">
<div class="buy-card">
<div class="buy-left">
<div class="buy-pretitle">Rēvo Condenser Microphone</div>
<div class="buy-name">Édition Fondateur</div>
<div class="buy-price">249 €</div>
<div class="buy-meta">
<span>Garantie 10 ans</span>
<span>·</span>
<span>Pièces à vie</span>
<span>·</span>
<span>Schémas inclus</span>
</div>
</div>
<button class="buy-btn">Précommander</button>
</div>
</div>
<!-- SCROLL SPACER -->
<div class="scroll-container"></div>
<script>
// ═══════════════════════════════════════════════
// FLOATING PARTICLES
// ═══════════════════════════════════════════════
const pCanvas = document.getElementById('particle-canvas');
const pCtx = pCanvas.getContext('2d');
let particles = [];
function initParticles() {
pCanvas.width = window.innerWidth;
pCanvas.height = window.innerHeight;
particles = [];
const count = Math.min(60, Math.floor(window.innerWidth / 25));
for (let i = 0; i < count; i++) {
particles.push({
x: Math.random() * pCanvas.width,
y: Math.random() * pCanvas.height,
vx: (Math.random() - 0.5) * 0.15,
vy: (Math.random() - 0.5) * 0.1 - 0.08,
size: Math.random() * 1.5 + 0.3,
opacity: Math.random() * 0.25 + 0.05,
pulse: Math.random() * Math.PI * 2,
});
}
}
function drawParticles() {
pCtx.clearRect(0, 0, pCanvas.width, pCanvas.height);
particles.forEach(p => {
p.x += p.vx;
p.y += p.vy;
p.pulse += 0.01;
if (p.y < -10) { p.y = pCanvas.height + 10; p.x = Math.random() * pCanvas.width; }
if (p.x < -10) p.x = pCanvas.width + 10;
if (p.x > pCanvas.width + 10) p.x = -10;
const o = p.opacity * (0.6 + 0.4 * Math.sin(p.pulse));
pCtx.beginPath();
pCtx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
pCtx.fillStyle = `rgba(196,135,92,${o})`;
pCtx.fill();
});
}
initParticles();
// ═══════════════════════════════════════════════
// THREE.JS SCENE
// ═══════════════════════════════════════════════
const container = document.getElementById('scene-container');
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 0.1, 100);
camera.position.set(0, 2, 9);
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true, powerPreference: 'high-performance' });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.0;
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
container.appendChild(renderer.domElement);
// ── LIGHTS ──
scene.add(new THREE.AmbientLight(0x887766, 0.2));
const key = new THREE.DirectionalLight(0xffeedd, 1.3);
key.position.set(4, 6, 5);
key.castShadow = true;
key.shadow.mapSize.set(1024, 1024);
key.shadow.bias = -0.001;
scene.add(key);
const fill = new THREE.DirectionalLight(0x6688aa, 0.35);
fill.position.set(-4, 3, -3);
scene.add(fill);
const rim = new THREE.SpotLight(0xc4875c, 0.8, 20, Math.PI / 6, 0.5);
rim.position.set(-2, -2, -5);
rim.target.position.set(0, 0, 0);
scene.add(rim);
scene.add(rim.target);
const topSpot = new THREE.SpotLight(0xffeedd, 0.5, 15, Math.PI / 8, 0.7);
topSpot.position.set(0, 8, 2);
topSpot.target.position.set(0, 0, 0);
topSpot.castShadow = true;
scene.add(topSpot);
scene.add(topSpot.target);
const bottomFill = new THREE.PointLight(0xc4875c, 0.2, 8);
bottomFill.position.set(0, -3, 3);
scene.add(bottomFill);
// ── REFLECTION PLANE ──
const planeGeo = new THREE.PlaneGeometry(20, 20);
const planeMat = new THREE.MeshStandardMaterial({
color: 0x060606,
metalness: 0.95,
roughness: 0.6,
transparent: true,
opacity: 0.5,
});
const plane = new THREE.Mesh(planeGeo, planeMat);
plane.rotation.x = -Math.PI / 2;
plane.position.y = -3.8;
plane.receiveShadow = true;
scene.add(plane);
// ── MATERIALS ──
const mat = {
body: new THREE.MeshStandardMaterial({ color: 0x141414, metalness: 0.92, roughness: 0.22 }),
bodyInner: new THREE.MeshStandardMaterial({ color: 0x1a1a1a, metalness: 0.85, roughness: 0.3 }),
grille: new THREE.MeshStandardMaterial({ color: 0x282828, metalness: 0.95, roughness: 0.18 }),
grilleWire: new THREE.MeshStandardMaterial({ color: 0x1e1e1e, metalness: 0.9, roughness: 0.25, wireframe: true }),
gold: new THREE.MeshStandardMaterial({ color: 0xc4875c, metalness: 0.96, roughness: 0.12 }),
goldBright: new THREE.MeshStandardMaterial({ color: 0xd4a574, metalness: 0.96, roughness: 0.1 }),
capsule: new THREE.MeshStandardMaterial({ color: 0x1c1c1c, metalness: 0.8, roughness: 0.35 }),
pcb: new THREE.MeshStandardMaterial({ color: 0x0f3a1a, metalness: 0.25, roughness: 0.55 }),
copper: new THREE.MeshStandardMaterial({ color: 0xb87333, metalness: 0.92, roughness: 0.25 }),
ic: new THREE.MeshStandardMaterial({ color: 0x080808, metalness: 0.5, roughness: 0.35 }),
cap: new THREE.MeshStandardMaterial({ color: 0x1a3a6a, metalness: 0.3, roughness: 0.5 }),
capBrown: new THREE.MeshStandardMaterial({ color: 0x6a3a1a, metalness: 0.3, roughness: 0.5 }),
silver: new THREE.MeshStandardMaterial({ color: 0x999999, metalness: 0.95, roughness: 0.2 }),
connector: new THREE.MeshStandardMaterial({ color: 0x1a1a1a, metalness: 0.88, roughness: 0.28 }),
};
// ── MIC GROUP ──
const micGroup = new THREE.Group();
scene.add(micGroup);
const parts = {
grille: { g: new THREE.Group(), baseY: 2.65, explodeY: 5.6, rotX: 0, rotZ: 0 },
capsule: { g: new THREE.Group(), baseY: 1.85, explodeY: 3.4, rotX: 0, rotZ: 0 },
pcb: { g: new THREE.Group(), baseY: 0.7, explodeY: 0.7, rotX: 0, rotZ: 0 },
body: { g: new THREE.Group(), baseY: -0.6, explodeY: -2.1, rotX: 0, rotZ: 0 },
connector: { g: new THREE.Group(), baseY: -2.2, explodeY: -4.6, rotX: 0, rotZ: 0 },
};
// ═══ GRILLE ═══
(() => {
const g = parts.grille.g;
// Outer dome
const dome = new THREE.Mesh(
new THREE.SphereGeometry(0.68, 48, 32, 0, Math.PI * 2, 0, Math.PI * 0.52),
mat.grille
);
g.add(dome);
// Inner wireframe
const inner = new THREE.Mesh(
new THREE.SphereGeometry(0.62, 20, 14, 0, Math.PI * 2, 0, Math.PI * 0.48),
mat.grilleWire
);
g.add(inner);
// Second inner layer
const inner2 = new THREE.Mesh(
new THREE.SphereGeometry(0.56, 12, 8, 0, Math.PI * 2, 0, Math.PI * 0.45),
new THREE.MeshStandardMaterial({ color: 0x161616, metalness: 0.85, roughness: 0.3, wireframe: true })
);
g.add(inner2);
// Base ring
const ring = new THREE.Mesh(new THREE.TorusGeometry(0.66, 0.05, 16, 64), mat.gold);
ring.rotation.x = Math.PI / 2;
ring.position.y = -0.16;
g.add(ring);
// Thin accent ring
const ring2 = new THREE.Mesh(new THREE.TorusGeometry(0.69, 0.015, 12, 64), mat.goldBright);
ring2.rotation.x = Math.PI / 2;
ring2.position.y = -0.2;
g.add(ring2);
g.position.y = parts.grille.baseY;
micGroup.add(g);
})();
// ═══ CAPSULE ═══
(() => {
const g = parts.capsule.g;
const housing = new THREE.Mesh(new THREE.CylinderGeometry(0.56, 0.62, 0.65, 48), mat.capsule);
g.add(housing);
// Diaphragm
const dia = new THREE.Mesh(new THREE.CylinderGeometry(0.42, 0.42, 0.025, 48), mat.goldBright);
dia.position.y = 0.2;
g.add(dia);
// Backplate
const bp = new THREE.Mesh(new THREE.CylinderGeometry(0.4, 0.4, 0.04, 48), mat.copper);
bp.position.y = 0.1;
g.add(bp);
// Rings
[0.3, -0.15].forEach(y => {
const r = new THREE.Mesh(new THREE.TorusGeometry(0.58, 0.02, 12, 48), mat.gold);
r.rotation.x = Math.PI / 2; r.position.y = y; g.add(r);
});
// Contact pins at bottom
for (let i = 0; i < 5; i++) {
const pin = new THREE.Mesh(new THREE.CylinderGeometry(0.015, 0.015, 0.2, 8), mat.gold);
const a = (i / 5) * Math.PI * 2;
pin.position.set(Math.cos(a) * 0.25, -0.42, Math.sin(a) * 0.25);
g.add(pin);
}
g.position.y = parts.capsule.baseY;
micGroup.add(g);
})();
// ═══ PCB ═══
(() => {
const g = parts.pcb.g;
// Board
const board = new THREE.Mesh(new THREE.BoxGeometry(0.9, 0.07, 0.9), mat.pcb);
g.add(board);
// Second smaller board underneath
const board2 = new THREE.Mesh(new THREE.BoxGeometry(0.7, 0.05, 0.5), mat.pcb);
board2.position.y = -0.08;
g.add(board2);
// Copper traces top
const tracePositions = [
{ w: 0.7, z: -0.35, r: 0 }, { w: 0.65, z: -0.22, r: 0.15 },
{ w: 0.5, z: -0.08, r: -0.1 }, { w: 0.6, z: 0.05, r: 0.05 },
{ w: 0.55, z: 0.18, r: -0.08 }, { w: 0.7, z: 0.32, r: 0 },
];
tracePositions.forEach(t => {
const trace = new THREE.Mesh(new THREE.BoxGeometry(t.w, 0.004, 0.025), mat.copper);
trace.position.set(0, 0.04, t.z);
trace.rotation.y = t.r;
g.add(trace);
});
// Cross traces
for (let i = 0; i < 4; i++) {
const ct = new THREE.Mesh(new THREE.BoxGeometry(0.02, 0.004, 0.5), mat.copper);
ct.position.set(-0.25 + i * 0.18, 0.04, 0);
g.add(ct);
}
// IC chip
const ic = new THREE.Mesh(new THREE.BoxGeometry(0.2, 0.06, 0.2), mat.ic);
ic.position.set(0, 0.065, 0.1);
g.add(ic);
// IC marking (tiny gold dot)
const icDot = new THREE.Mesh(new THREE.SphereGeometry(0.015, 8, 8), mat.gold);
icDot.position.set(-0.06, 0.1, 0.05);
g.add(icDot);
// Electrolytic caps
[{ x: 0.28, z: -0.22, h: 0.18, r: 0.045, m: mat.cap },
{ x: -0.3, z: 0.25, h: 0.14, r: 0.038, m: mat.capBrown },
{ x: 0.32, z: 0.2, h: 0.12, r: 0.032, m: mat.cap }].forEach(c => {
const cap = new THREE.Mesh(new THREE.CylinderGeometry(c.r, c.r, c.h, 16), c.m);
cap.position.set(c.x, 0.04 + c.h / 2, c.z);
g.add(cap);
// Cap top ring
const cr = new THREE.Mesh(new THREE.TorusGeometry(c.r * 0.8, 0.005, 8, 16), mat.silver);
cr.rotation.x = Math.PI / 2;
cr.position.set(c.x, 0.04 + c.h, c.z);
g.add(cr);
});
// SMD resistors
for (let i = 0; i < 8; i++) {
const smd = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.02, 0.03), mat.ic);
const a = (i / 8) * Math.PI * 2;
smd.position.set(Math.cos(a) * 0.18, 0.045, Math.sin(a) * 0.18);
smd.rotation.y = a;
g.add(smd);
}
// Solder pads
for (let i = 0; i < 12; i++) {
const sp = new THREE.Mesh(new THREE.CylinderGeometry(0.012, 0.012, 0.008, 8), mat.silver);
const a = (i / 12) * Math.PI * 2;
sp.position.set(Math.cos(a) * 0.36, 0.04, Math.sin(a) * 0.36);
g.add(sp);
}
// Connector header
const header = new THREE.Mesh(new THREE.BoxGeometry(0.15, 0.08, 0.06), new THREE.MeshStandardMaterial({ color: 0x222222, roughness: 0.7 }));
header.position.set(-0.2, 0.075, -0.32);
g.add(header);
for (let i = 0; i < 5; i++) {
const hp = new THREE.Mesh(new THREE.CylinderGeometry(0.008, 0.008, 0.12, 6), mat.gold);
hp.position.set(-0.24 + i * 0.025, 0.095, -0.32);
g.add(hp);
}
g.position.y = parts.pcb.baseY;
micGroup.add(g);
})();
// ═══ BODY ═══
(() => {
const g = parts.body.g;
// Main body
const body = new THREE.Mesh(new THREE.CylinderGeometry(0.6, 0.55, 2.2, 48), mat.body);
body.castShadow = true;
g.add(body);
// Internal bore
const bore = new THREE.Mesh(
new THREE.CylinderGeometry(0.48, 0.45, 2.22, 32),
new THREE.MeshStandardMaterial({ color: 0x0c0c0c, metalness: 0.7, roughness: 0.4, side: THREE.BackSide })
);
g.add(bore);
// Gold accent rings
const ringYs = [0.85, 0.2, -0.45, -0.9];
ringYs.forEach((y, i) => {
const thickness = i === 0 ? 0.035 : 0.02;
const r = new THREE.Mesh(new THREE.TorusGeometry(0.605 - (i > 1 ? 0.01 : 0), thickness, 12, 64), mat.gold);
r.rotation.x = Math.PI / 2;
r.position.y = y;
g.add(r);
});
// Knurling texture band
for (let i = 0; i < 80; i++) {
const a = (i / 80) * Math.PI * 2;
const knurl = new THREE.Mesh(new THREE.BoxGeometry(0.008, 0.35, 0.015), mat.bodyInner);
knurl.position.set(Math.cos(a) * 0.605, -0.2, Math.sin(a) * 0.605);
knurl.rotation.y = -a;
g.add(knurl);
}
// Visible screws
for (let i = 0; i < 4; i++) {
const a = (i / 4) * Math.PI * 2 + Math.PI / 4;
// Screw head
const head = new THREE.Mesh(new THREE.CylinderGeometry(0.032, 0.032, 0.02, 6), mat.silver);
head.position.set(Math.cos(a) * 0.61, -0.65, Math.sin(a) * 0.61);
head.lookAt(0, -0.65, 0);
g.add(head);
// Slot
const slot = new THREE.Mesh(
new THREE.BoxGeometry(0.04, 0.003, 0.008),
new THREE.MeshStandardMaterial({ color: 0x333333 })
);
slot.position.copy(head.position);
slot.lookAt(0, -0.65, 0);
g.add(slot);
}
g.position.y = parts.body.baseY;
micGroup.add(g);
})();
// ═══ CONNECTOR ═══
(() => {
const g = parts.connector.g;
// Taper
const taper = new THREE.Mesh(new THREE.CylinderGeometry(0.55, 0.38, 0.55, 48), mat.connector);
taper.position.y = 0.2;
taper.castShadow = true;
g.add(taper);
// XLR barrel
const xlr = new THREE.Mesh(new THREE.CylinderGeometry(0.35, 0.35, 0.45, 48), mat.connector);
xlr.position.y = -0.2;
g.add(xlr);
// Gold rings
[0.0, -0.42].forEach(y => {
const r = new THREE.Mesh(new THREE.TorusGeometry(0.36, 0.02, 12, 48), mat.gold);
r.rotation.x = Math.PI / 2; r.position.y = y; g.add(r);
});
// XLR pins
for (let i = 0; i < 3; i++) {
const a = (i / 3) * Math.PI * 2 - Math.PI / 2;
const pin = new THREE.Mesh(new THREE.CylinderGeometry(0.022, 0.022, 0.18, 8), mat.goldBright);
pin.position.set(Math.cos(a) * 0.14, -0.52, Math.sin(a) * 0.14);
g.add(pin);
}
// Ground pin (center)
const gndPin = new THREE.Mesh(new THREE.CylinderGeometry(0.012, 0.012, 0.22, 8), mat.silver);
gndPin.position.set(0, -0.53, 0);
g.add(gndPin);
// Internal thread lines
for (let i = 0; i < 3; i++) {
const tr = new THREE.Mesh(new THREE.TorusGeometry(0.33, 0.005, 6, 32), mat.silver);
tr.rotation.x = Math.PI / 2;
tr.position.y = -0.1 - i * 0.12;
g.add(tr);
}
g.position.y = parts.connector.baseY;
micGroup.add(g);
})();
micGroup.position.y = -0.3;
micGroup.rotation.x = 0.12;
micGroup.rotation.y = -0.5;
// ═══════════════════════════════════════════════
// SCROLL ENGINE
// ═══════════════════════════════════════════════
const heroEl = document.getElementById('hero-content');
const scrollCta = document.getElementById('scroll-cta');
const buyEl = document.getElementById('buy-section');
const repairEl = document.getElementById('repair-floating');
const prfEl = document.getElementById('prf');
const prlEls = document.querySelectorAll('.prl');
const annoEls = {
grille: document.getElementById('anno-grille'),
capsule: document.getElementById('anno-capsule'),
pcb: document.getElementById('anno-pcb'),
body: document.getElementById('anno-body'),
connector: document.getElementById('anno-connector'),
};
// Connector line elements
const clEls = {}, cdEls = {};
['grille','capsule','pcb','body','connector'].forEach(k => {
clEls[k] = document.getElementById('cl-' + k);
cdEls[k] = document.getElementById('cd-' + k);
});
function ease(t) { return t < 0.5 ? 4*t*t*t : 1 - Math.pow(-2*t+2,3)/2; }
function easeOut5(t) { return 1 - Math.pow(1-t,5); }
function clamp(v, a, b) { return Math.max(a, Math.min(b, v)); }
function lerp(a, b, t) { return a + (b - a) * clamp(t, 0, 1); }
function map(v, a, b, c, d) { return c + (d-c) * clamp((v-a)/(b-a), 0, 1); }
let sp = 0, autoRot = 0, spinning = false;
// Project 3D to screen
function project(vec3) {
const v = vec3.clone().project(camera);
return {
x: (v.x * 0.5 + 0.5) * window.innerWidth,
y: (-v.y * 0.5 + 0.5) * window.innerHeight,
};
}
function updateConnectorLines() {
const partScreenPos = {};
const partKeys = ['grille','capsule','pcb','body','connector'];
partKeys.forEach(k => {
const wp = new THREE.Vector3();
parts[k].g.getWorldPosition(wp);
partScreenPos[k] = project(wp);
});
// Annotation positions
const annoPositions = {
grille: { side: 'right', el: annoEls.grille },
capsule: { side: 'left', el: annoEls.capsule },
pcb: { side: 'right', el: annoEls.pcb },
body: { side: 'left', el: annoEls.body },
connector: { side: 'right', el: annoEls.connector },
};
partKeys.forEach(k => {
const pp = partScreenPos[k];
const ap = annoPositions[k];
const rect = ap.el.getBoundingClientRect();
const visible = ap.el.classList.contains('visible');
let lx, ly;
if (ap.side === 'right') {
lx = rect.left;
ly = rect.top + rect.height * 0.35;
} else {
lx = rect.right;
ly = rect.top + rect.height * 0.35;
}
clEls[k].setAttribute('x1', pp.x);
clEls[k].setAttribute('y1', pp.y);
clEls[k].setAttribute('x2', lx);
clEls[k].setAttribute('y2', ly);
cdEls[k].setAttribute('cx', pp.x);
cdEls[k].setAttribute('cy', pp.y);
clEls[k].classList.toggle('visible', visible);
cdEls[k].classList.toggle('visible', visible);
});
}
function updateScene() {
const scrollY = window.scrollY;
const maxScroll = document.documentElement.scrollHeight - window.innerHeight;
sp = clamp(scrollY / maxScroll, 0, 1);
// Progress rail
prfEl.style.height = (sp * 100) + '%';
const sections = [0, 0.10, 0.20, 0.32, 0.44, 0.56, 0.80];
prlEls.forEach((el, i) => {
const active = sp >= sections[i] && (i === sections.length - 1 || sp < sections[i+1]);
el.classList.toggle('active', active);
});
// Hero
heroEl.style.opacity = map(sp, 0, 0.07, 1, 0);
heroEl.style.transform = `translateY(calc(-50% + ${sp * -120}px))`;
scrollCta.style.opacity = map(sp, 0, 0.04, 1, 0);
// ── EXPLODE PHASES ──
const eStart = 0.07;
const eEnd = 0.58;
const rStart = 0.72;
const rEnd = 0.82;
let ef;
if (sp < eStart) ef = 0;
else if (sp < eEnd) ef = ease(map(sp, eStart, eEnd, 0, 1));
else if (sp < rStart) ef = 1;
else if (sp < rEnd) ef = 1 - easeOut5(map(sp, rStart, rEnd, 0, 1));
else ef = 0;
const partKeys = ['grille','capsule','pcb','body','connector'];
const stagger = [0, 0.035, 0.07, 0.11, 0.15];
partKeys.forEach((k, i) => {
const p = parts[k];
let pf;
if (sp < rStart) {
const ps = eStart + stagger[i];
const pe = ps + (eEnd - eStart) * 0.55;
pf = ease(clamp((sp - ps) / (pe - ps), 0, 1));
} else {
pf = ef;
}
p.g.position.y = lerp(p.baseY, p.explodeY, pf);
// Individual part rotations during explode
if (k === 'pcb') {
p.g.rotation.y = pf * 0.25;
p.g.rotation.x = pf * -0.08;
}
if (k === 'grille') p.g.rotation.z = pf * 0.08;
if (k === 'connector') p.g.rotation.z = pf * -0.05;
});
// ── ANNOTATIONS ──
const annoRanges = {
grille: [0.10, 0.22],
capsule: [0.18, 0.32],
pcb: [0.28, 0.48],
body: [0.38, 0.54],
connector: [0.48, 0.64],
};
Object.entries(annoRanges).forEach(([k, [s, e]]) => {
const el = annoEls[k];
const vis = sp >= s && sp <= e + 0.06;
const o = vis
? map(sp, s, s + 0.035, 0, 1) * map(sp, e, e + 0.06, 1, 0)
: 0;
el.style.opacity = clamp(o, 0, 1);
el.classList.toggle('visible', o > 0.1);
});
// Repair badge
const repVis = sp > 0.42 && sp < 0.66;
repairEl.classList.toggle('visible', repVis);
// ── CAMERA CHOREOGRAPHY ──
if (sp < 0.72) {
camera.position.x = lerp(0, -0.8, map(sp, 0, 0.55, 0, 1));
camera.position.y = lerp(2, 0.8, map(sp, 0, 0.55, 0, 1));
camera.position.z = lerp(9, 10.5, map(sp, 0, 0.55, 0, 1));
} else {
camera.position.x = lerp(-0.8, 0, map(sp, 0.72, 0.85, 0, 1));
camera.position.y = lerp(0.8, 1.2, map(sp, 0.72, 0.85, 0, 1));
camera.position.z = lerp(10.5, 8, map(sp, 0.72, 0.85, 0, 1));
}
camera.lookAt(0, 0.2, 0);
// Mic base rotation
if (sp < 0.72) {
micGroup.rotation.y = -0.5 + sp * 1.5;
spinning = false;
} else {
spinning = true;
}
// Buy
buyEl.classList.toggle('visible', sp > 0.87);
// Update connector lines
updateConnectorLines();
}
// ── RENDER LOOP ──
function animate() {
requestAnimationFrame(animate);
if (spinning) {
autoRot += 0.008;
micGroup.rotation.y = autoRot;
} else {
autoRot = micGroup.rotation.y;
}
drawParticles();
renderer.render(scene, camera);
}
window.addEventListener('scroll', updateScene, { passive: true });
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
pCanvas.width = window.innerWidth;
pCanvas.height = window.innerHeight;
});
updateScene();
animate();
</script>
</body>
</html>