Просмотр исходного кода

Обновить 'docs/file.md'

u23-27dybenko 1 неделя назад
Родитель
Сommit
685ac622dc
1 измененных файлов с 419 добавлено и 263 удалено
  1. 419 263
      docs/file.md

+ 419 - 263
docs/file.md

@@ -184,6 +184,96 @@
     border:1px solid var(--neon-purple);
     border:1px solid var(--neon-purple);
     background:linear-gradient(145deg,rgba(162,89,255,.15),rgba(0,229,255,.08));
     background:linear-gradient(145deg,rgba(162,89,255,.15),rgba(0,229,255,.08));
     box-shadow:0 0 20px rgba(162,89,255,.4);font-weight:700;}
     box-shadow:0 0 20px rgba(162,89,255,.4);font-weight:700;}
+      /* 🎨 Карточки с PNG-картинками */
+  .card-face{
+    flex-direction: column;
+    gap: 12px;
+    padding: 16px;
+  }
+  .card-face .card-img{
+    width: 72px;
+    height: 72px;
+    object-fit: contain;
+    animation: cardFloat 3s ease-in-out infinite;
+    transition: transform .3s;
+  }
+  @keyframes cardFloat{
+    0%,100%{transform: translateY(0) scale(1);}
+    50%{transform: translateY(-4px) scale(1.04);}
+  }
+  .card-face .card-title{
+    font-size: .95rem;
+    letter-spacing: 1.5px;
+    text-transform: uppercase;
+    font-weight: 800;
+  }
+
+  /* 🦇 Эхолокация — зелёный неон */
+  .card.eco .card-face{
+    background: linear-gradient(145deg, rgba(57,255,20,.15), rgba(0,229,255,.06));
+    border-color: var(--neon-green);
+    box-shadow: 0 0 25px rgba(57,255,20,.35);
+    color: var(--neon-green);
+  }
+  .card.eco .card-img{
+    filter: drop-shadow(0 0 14px #39ff14) drop-shadow(0 0 6px #39ff14);
+  }
+  .card.eco .card-face.back{
+    color: #d8ffd0;
+    border-color: var(--neon-cyan);
+  }
+
+  /* 🩻 УЗИ — циан */
+  .card.uzi .card-face{
+    background: linear-gradient(145deg, rgba(0,229,255,.15), rgba(162,89,255,.06));
+    border-color: var(--neon-cyan);
+    box-shadow: 0 0 25px rgba(0,229,255,.35);
+    color: var(--neon-cyan);
+  }
+  .card.uzi .card-img{
+    filter: drop-shadow(0 0 14px #00e5ff) drop-shadow(0 0 6px #00e5ff);
+  }
+  .card.uzi .card-face.back{
+    color: #cfeaff;
+    border-color: var(--neon-purple);
+  }
+
+  /* 🌍 Сейсмология — фиолетовый */
+  .card.seismo .card-face{
+    background: linear-gradient(145deg, rgba(162,89,255,.15), rgba(255,46,196,.06));
+    border-color: var(--neon-purple);
+    box-shadow: 0 0 25px rgba(162,89,255,.35);
+    color: var(--neon-purple);
+  }
+  .card.seismo .card-img{
+    filter: drop-shadow(0 0 14px #a259ff) drop-shadow(0 0 6px #a259ff);
+  }
+  .card.seismo .card-face.back{
+    color: #e8d5ff;
+    border-color: var(--neon-pink);
+  }
+
+  /* 🎸 Музыка — розовый */
+  .card.music .card-face{
+    background: linear-gradient(145deg, rgba(255,46,196,.15), rgba(0,229,255,.06));
+    border-color: var(--neon-pink);
+    box-shadow: 0 0 25px rgba(255,46,196,.35);
+    color: var(--neon-pink);
+  }
+  .card.music .card-img{
+    filter: drop-shadow(0 0 14px #ff2ec4) drop-shadow(0 0 6px #ff2ec4);
+  }
+  .card.music .card-face.back{
+    color: #ffd5f4;
+    border-color: var(--neon-cyan);
+  }
+
+  /* Задняя сторона — без покачивания */
+  .card-face.back .card-img{
+    width: 40px;
+    height: 40px;
+    animation: none;
+  }
   .card-face.back{transform:rotateY(180deg);font-weight:400;font-size:.85rem;
   .card-face.back{transform:rotateY(180deg);font-weight:400;font-size:.85rem;
     border-color:var(--neon-cyan);color:#cfeaff;}
     border-color:var(--neon-cyan);color:#cfeaff;}
   .pairs{display:grid;grid-template-columns:1fr 1fr;gap:24px;width:min(700px,100%);margin:16px 0;}
   .pairs{display:grid;grid-template-columns:1fr 1fr;gap:24px;width:min(700px,100%);margin:16px 0;}
@@ -269,6 +359,28 @@
   #quizResult{position:relative;z-index:10;}
   #quizResult{position:relative;z-index:10;}
   .hint{font-size:.8rem;color:var(--neon-pink);letter-spacing:1px;
   .hint{font-size:.8rem;color:var(--neon-pink);letter-spacing:1px;
     margin-top:8px;min-height:1.2em;}
     margin-top:8px;min-height:1.2em;}
+      .track-slot{
+    width:70px;height:70px;border-radius:12px;
+    display:flex;align-items:center;justify-content:center;
+    font-size:2rem;
+    border:2px dashed rgba(0,229,255,.4);
+    background:rgba(0,229,255,.05);
+    color:rgba(200,220,255,.3);
+    transition:.3s;
+  }
+  .track-slot.filled{
+    border-style:solid;
+    border-color:var(--neon-green);
+    background:rgba(57,255,20,.1);
+    box-shadow:0 0 20px rgba(57,255,20,.5);
+    color:#fff;
+    animation:pop .4s cubic-bezier(.2,1.4,.5,1);
+  }
+  #instrBtns .btn.active{
+    background:var(--neon-pink);
+    color:#000;
+    box-shadow:0 0 25px var(--neon-pink);
+  }
   .table{font-size:.85rem;color:#cfeaff;margin-top:8px;text-align:center;line-height:1.8;}
   .table{font-size:.85rem;color:#cfeaff;margin-top:8px;text-align:center;line-height:1.8;}
   .table span{color:var(--neon-cyan);font-weight:700;}
   .table span{color:var(--neon-cyan);font-weight:700;}
 </style>
 </style>
@@ -359,66 +471,128 @@
     <button class="btn" id="btnNext4" type="button" disabled>Далее →</button>
     <button class="btn" id="btnNext4" type="button" disabled>Далее →</button>
   </section>
   </section>
 
 
-    <!-- SCENE 5 -->
+        <!-- SCENE 5 -->
   <section class="scene" id="scene5">
   <section class="scene" id="scene5">
     <h2>Виды волн. Звук — продольная волна</h2>
     <h2>Виды волн. Звук — продольная волна</h2>
 
 
-    <div class="controls">
-      <button class="btn" id="btnLong" type="button">Продольная</button>
-      <button class="btn pink" id="btnTrans" type="button">Поперечная</button>
+    <div class="canvas-wrap">
+      <canvas id="typeCanvas" width="800" height="320"></canvas>
     </div>
     </div>
 
 
-    <div class="canvas-wrap" style="position:relative;">
-      <canvas id="typeCanvas" width="800" height="220"></canvas>
-    </div>
-
-    <!-- 🎮 Мини-игра -->
-    <div id="waveGame" style="max-width:720px;text-align:center;">
+    <!-- 🎮 Мини-игра «Собери звуковую дорожку» -->
+    <div id="waveGame" style="max-width:760px;text-align:center;">
       <p style="margin-bottom:8px;">
       <p style="margin-bottom:8px;">
-        🎯 <b>Мини-игра:</b> нажми <b>«Толкнуть поршень»</b> и кликай по
-        <span style="color:var(--neon-pink);font-weight:800;">розовым сгусткам (сжатиям)</span>,
-        когда они проплывают мимо.
+        🎧 <b>Мини-игра:</b> нажми на каждый инструмент по очереди — собери <b>дорожку из 4 звуков</b>.
+        Смотри, как меняется волна!
       </p>
       </p>
-      <div class="controls">
-        <button class="btn green" id="btnPiston" type="button">🔊 Толкнуть поршень</button>
-        <button class="btn pink" id="btnReset5" type="button">🔄 Сброс</button>
+
+      <div class="controls" id="instrBtns">
+        <button class="btn" data-instr="piano" type="button">🎹 Пианино</button>
+        <button class="btn" data-instr="drum"  type="button">🥁 Барабан</button>
+        <button class="btn" data-instr="whistle" type="button">🎺 Свисток</button>
+        <button class="btn" data-instr="bass"  type="button">🎸 Бас</button>
+      </div>
+
+      <div id="instrFreq" class="hint" style="font-size:.95rem;">
+        🎵 Нажми на инструмент, чтобы услышать и увидеть волну
+      </div>
+
+      <!-- Дорожка со слотами -->
+      <div id="track" style="display:flex;gap:10px;justify-content:center;margin:16px 0;">
+        <div class="track-slot" data-slot="0">—</div>
+        <div class="track-slot" data-slot="1">—</div>
+        <div class="track-slot" data-slot="2">—</div>
+        <div class="track-slot" data-slot="3">—</div>
       </div>
       </div>
-      <p class="hint" id="scene5Hint">Поймано сжатий: 0 / 5</p>
+
+      <p class="hint" id="scene5Hint">Собрано звуков: 0 / 4</p>
     </div>
     </div>
 
 
     <!-- 💡 Пояснение (появляется после победы) -->
     <!-- 💡 Пояснение (появляется после победы) -->
     <div id="waveFact" class="table" style="max-width:720px;display:none;">
     <div id="waveFact" class="table" style="max-width:720px;display:none;">
-      🎓 <b>Что ты сейчас сделал:</b><br>
-      Поршень толкает молекулы → они <span>сжимаются</span>, потом расходятся → возникает
-      <span>разрежение</span>. Волна <span>бежит вправо</span>, а каждая молекула
-      <span>колеблется на месте</span> и никуда не улетает.<br>
-      🔊 Именно так звук распространяется через воздух — продольной волной!
+      🎓 <b>Разные звуки = разные волны.</b><br>
+      🔵 <b>Продольная</b> (сверху) — сжатия и разрежения. Так идёт звук в воздухе.<br>
+      🟢 <b>Поперечная</b> (снизу) — гребни и впадины. Так колеблется струна или волна на воде.<br>
+      🔊 Обе волны переносят <span>энергию</span>, а частицы среды <span>колеблются на месте</span>.
     </div>
     </div>
 
 
     <button class="btn" id="btnNext5" type="button" disabled>Далее →</button>
     <button class="btn" id="btnNext5" type="button" disabled>Далее →</button>
   </section>
   </section>
-
+  
   <!-- SCENE 6 -->
   <!-- SCENE 6 -->
   <section class="scene" id="scene6">
   <section class="scene" id="scene6">
     <h2>Применение звуковых волн</h2>
     <h2>Применение звуковых волн</h2>
     <p>Нажми на карточку, чтобы узнать подробнее.</p>
     <p>Нажми на карточку, чтобы узнать подробнее.</p>
-    <div class="cards" id="cards">
-      <div class="card"><div class="card-inner">
-        <div class="card-face">Эхолокация</div>
-        <div class="card-face back">Летучие мыши и дельфины определяют препятствия по отражённому ультразвуку.</div>
+        <div class="cards" id="cards">
+
+      <div class="card eco"><div class="card-inner">
+        <div class="card-face">
+          <img class="card-img"
+               src="https://cdn-icons-png.flaticon.com/512/616/616554.png"
+               alt="Эхолокация"
+               onerror="this.replaceWith(Object.assign(document.createElement('div'),{textContent:'🦇',style:'font-size:3.4rem;line-height:1;'}));" />
+          <div class="card-title">Эхолокация</div>
+        </div>
+        <div class="card-face back">
+          <img class="card-img"
+               src="https://cdn-icons-png.flaticon.com/512/616/616554.png"
+               alt="Эхолокация"
+               onerror="this.replaceWith(Object.assign(document.createElement('div'),{textContent:'🦇',style:'font-size:1.4rem;line-height:1;'}));" />
+          <div>Летучие мыши и дельфины определяют препятствия по отражённому ультразвуку.</div>
+        </div>
       </div></div>
       </div></div>
-      <div class="card"><div class="card-inner">
-        <div class="card-face">УЗИ</div>
-        <div class="card-face back">Медицинская визуализация: ультразвук отражается от границ тканей.</div>
+
+      <div class="card uzi"><div class="card-inner">
+        <div class="card-face">
+          <img class="card-img"
+               src="https://cdn-icons-png.flaticon.com/512/2966/2966327.png"
+               alt="УЗИ"
+               onerror="this.replaceWith(Object.assign(document.createElement('div'),{textContent:'🩻',style:'font-size:3.4rem;line-height:1;'}));" />
+          <div class="card-title">УЗИ</div>
+        </div>
+        <div class="card-face back">
+          <img class="card-img"
+               src="https://cdn-icons-png.flaticon.com/512/2966/2966327.png"
+               alt="УЗИ"
+               onerror="this.replaceWith(Object.assign(document.createElement('div'),{textContent:'🩻',style:'font-size:1.4rem;line-height:1;'}));" />
+          <div>Медицинская визуализация: ультразвук отражается от границ тканей.</div>
+        </div>
       </div></div>
       </div></div>
-      <div class="card"><div class="card-inner">
-        <div class="card-face">Сейсмология</div>
-        <div class="card-face back">По сейсмическим волнам изучают строение Земли и землетрясения.</div>
+
+      <div class="card seismo"><div class="card-inner">
+        <div class="card-face">
+          <img class="card-img"
+               src="https://cdn-icons-png.flaticon.com/512/2909/2909760.png"
+               alt="Сейсмология"
+               onerror="this.replaceWith(Object.assign(document.createElement('div'),{textContent:'🌍',style:'font-size:3.4rem;line-height:1;'}));" />
+          <div class="card-title">Сейсмология</div>
+        </div>
+        <div class="card-face back">
+          <img class="card-img"
+               src="https://cdn-icons-png.flaticon.com/512/2909/2909760.png"
+               alt="Сейсмология"
+               onerror="this.replaceWith(Object.assign(document.createElement('div'),{textContent:'🌍',style:'font-size:1.4rem;line-height:1;'}));" />
+          <div>По сейсмическим волнам изучают строение Земли и землетрясения.</div>
+        </div>
       </div></div>
       </div></div>
-      <div class="card"><div class="card-inner">
-        <div class="card-face">Музыка</div>
-        <div class="card-face back">Струны и столбы воздуха создают стоячие волны — ноты.</div>
+
+      <div class="card music"><div class="card-inner">
+        <div class="card-face">
+          <img class="card-img"
+               src="https://cdn-icons-png.flaticon.com/512/384/384281.png"
+               alt="Музыка"
+               onerror="this.replaceWith(Object.assign(document.createElement('div'),{textContent:'🎸',style:'font-size:3.4rem;line-height:1;'}));" />
+          <div class="card-title">Музыка</div>
+        </div>
+        <div class="card-face back">
+          <img class="card-img"
+               src="https://cdn-icons-png.flaticon.com/512/384/384281.png"
+               alt="Музыка"
+               onerror="this.replaceWith(Object.assign(document.createElement('div'),{textContent:'🎸',style:'font-size:1.4rem;line-height:1;'}));" />
+          <div>Струны и столбы воздуха создают стоячие волны — ноты.</div>
+        </div>
       </div></div>
       </div></div>
+
     </div>
     </div>
     <p>Собери пару: пример ↔ физическое явление.</p>
     <p>Собери пару: пример ↔ физическое явление.</p>
     <div class="pairs">
     <div class="pairs">
@@ -828,278 +1002,260 @@ document.getElementById('btnCheck4').addEventListener('click', function(){
 });
 });
 document.getElementById('btnNext4').addEventListener('click', function(){ goToScene(4); });
 document.getElementById('btnNext4').addEventListener('click', function(){ goToScene(4); });
 
 
-/* ================= SCENE 5: виды волн + мини-игра ================= */
+/* ================= SCENE 5: две волны одновременно + мини-игра ================= */
 var typeCanvas = document.getElementById('typeCanvas');
 var typeCanvas = document.getElementById('typeCanvas');
 var typeCtx = typeCanvas.getContext('2d');
 var typeCtx = typeCanvas.getContext('2d');
 var typeT = 0;
 var typeT = 0;
-var waveType = 'long';       // 'long' | 'trans'
-
-// ---- Мини-игра ----
-var gameActive = false;      // идёт ли волна сейчас
-var gameCaught = 0;          // сколько сжатий поймано
-var gamePhase = 0;           // фаза волны (0..2π повторяется)
-var gameSpeed = 0.06;        // скорость бега волны
-var gameBursts = [];         // визуальные "вспышки" от кликов
-var gameZones = [];          // текущие зоны сжатий (пересчитывается каждый кадр)
-
-// Параметры волны
-var WAVE_N = 50;             // сколько молекул
-var WAVE_LEN = 4;            // сколько длин волн на экране
-
-// 🔘 Переключатели вида волны
-document.getElementById('btnLong').addEventListener('click', function(){
-  waveType = 'long'; clickSound();
-  document.getElementById('waveGame').style.display = 'block';
-});
-document.getElementById('btnTrans').addEventListener('click', function(){
-  waveType = 'trans'; clickSound();
-  // В поперечной волне мини-игра не нужна — прячем
-  document.getElementById('waveGame').style.display = 'none';
-});
 
 
-// 🎮 Кнопка "Толкнуть поршень"
-document.getElementById('btnPiston').addEventListener('click', function(){
-  if(gameActive) return;                 // уже идёт
-  gameActive = true;
-  gamePhase = 0;
-  gameCaught = 0;
-  gameBursts = [];
-  document.getElementById('scene5Hint').textContent = 'Поймано сжатий: 0 / 5';
-  document.getElementById('waveFact').style.display = 'none';
-  document.getElementById('btnNext5').disabled = true;
-  ensureAudio();
-  beep(180, 0.25, 'sawtooth', 0.20);      // низкий "толчок поршня"
-});
+// 🎼 Инструменты
+var INSTRUMENTS = {
+  piano:   { name:'Пианино', emoji:'🎹', freq:261.63,  wave:'sine',   color:'#00e5ff', desc:'261 Гц · До первой октавы' },
+  drum:    { name:'Барабан', emoji:'🥁', freq:80,      wave:'noise',  color:'#ff2ec4', desc:'~80 Гц · шумный удар' },
+  whistle: { name:'Свисток', emoji:'🎺', freq:1046.50, wave:'sine',   color:'#39ff14', desc:'1046 Гц · До второй октавы (высокий)' },
+  bass:    { name:'Бас',     emoji:'🎸', freq:130.81,  wave:'square', color:'#a259ff', desc:'130 Гц · До малой октавы (низкий)' }
+};
 
 
-// 🔄 Кнопка "Сброс"
-document.getElementById('btnReset5').addEventListener('click', function(){
-  gameActive = false;
-  gameCaught = 0;
-  gamePhase = 0;
-  gameBursts = [];
-  document.getElementById('scene5Hint').textContent = 'Поймано сжатий: 0 / 5';
-  document.getElementById('waveFact').style.display = 'none';
-  document.getElementById('btnNext5').disabled = true;
-  clickSound();
-});
+var track = [];
+var currentInstr = null;
+var gameWin = false;
+
+// 🔘 Клики по инструментам
+var instrBtns = document.querySelectorAll('#instrBtns .btn');
+for(var ib=0; ib<instrBtns.length; ib++){
+  (function(btn){
+        btn.addEventListener('click', function(){
+      var key = btn.dataset.instr;
+      var instr = INSTRUMENTS[key];
+      if(!instr) return;
+            // Уже нажат — просто заменим волну, не добавляем в дорожку повторно
+      var alreadyIn = track.indexOf(key) !== -1;
+
+            // 🌊 Всегда переключаем текущий инструмент — волна меняется сразу
+      currentInstr = key;
+
+      // 📥 Добавляем в дорожку ТОЛЬКО если ещё не было
+      if(!alreadyIn){
+        track.push(key);
+
+        var slot = document.querySelector('.track-slot[data-slot="' + (track.length - 1) + '"]');
+        if(slot){
+          slot.textContent = instr.emoji;
+          slot.classList.add('filled');
+        }
+      }
+
+      // 🎨 Подсветка кнопки
+      for(var k=0;k<instrBtns.length;k++) instrBtns[k].classList.remove('active');
+      btn.classList.add('active');
+
+      // 🔊 Звук играем всегда (даже при повторном клике)
+      playInstrumentSound(key);
+
+      // 📝 Инфо-подпись
+      document.getElementById('instrFreq').textContent =
+        instr.emoji + ' ' + instr.name + ' — ' + instr.desc;
 
 
-// 👆 Клик по canvas — попытка поймать сжатие
-typeCanvas.addEventListener('click', function(e){
-  if(!gameActive || waveType !== 'long') return;
-  var rect = typeCanvas.getBoundingClientRect();
-  var scaleX = typeCanvas.width / rect.width;
-  var clickX = (e.clientX - rect.left) * scaleX;
-
-  // Ищем, попал ли клик в зону сжатия
-  var hit = false;
-  for(var i=0; i<gameZones.length; i++){
-    var z = gameZones[i];
-    if(clickX >= z.x - z.w/2 && clickX <= z.x + z.w/2){
-      hit = true;
-      break;
-    }
-  }
-  if(hit){
-    gameCaught++;
-    correctSound();
-    gameBursts.push({ x: clickX, y: typeCanvas.height/2, r: 10, alpha: 1 });
-    document.getElementById('scene5Hint').textContent =
-      'Поймано сжатий: ' + gameCaught + ' / 5';
-    if(gameCaught >= 5){
-      gameActive = false;
       document.getElementById('scene5Hint').textContent =
       document.getElementById('scene5Hint').textContent =
-        '🎉 Отлично! Ты собрал продольную волну!';
-      document.getElementById('waveFact').style.display = 'block';
-      document.getElementById('btnNext5').disabled = false;
-      correctSound();
-      setTimeout(function(){ beep(1320, 0.4, 'triangle', 0.2); }, 200);
-    }
+        'Собрано звуков: ' + track.length + ' / 4';
+
+      // 🎉 Победа — только один раз
+      if(track.length >= 4 && !gameWin){
+        gameWin = true;
+        setTimeout(function(){
+          document.getElementById('scene5Hint').textContent =
+            '🎉 Мелодия собрана! Меняй инструменты — волна меняется!';
+          document.getElementById('waveFact').style.display = 'block';
+          document.getElementById('btnNext5').disabled = false;
+          playMelody();
+        }, 400);
+      }
+    });
+  })(instrBtns[ib]);
+}
+
+// 🔊 Синтез звука инструмента
+function playInstrumentSound(key){
+  if(!soundOn) return;
+  ensureAudio();
+  if(!audioCtx) return;
+  var instr = INSTRUMENTS[key];
+
+  if(instr.wave === 'noise'){
+    var bufferSize = audioCtx.sampleRate * 0.2;
+    var buffer = audioCtx.createBuffer(1, bufferSize, audioCtx.sampleRate);
+    var data = buffer.getChannelData(0);
+    for(var i=0;i<bufferSize;i++) data[i] = (Math.random()*2 - 1);
+    var src = audioCtx.createBufferSource();
+    src.buffer = buffer;
+    var g = audioCtx.createGain();
+    g.gain.setValueAtTime(0.25, audioCtx.currentTime);
+    g.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + 0.2);
+    src.connect(g); g.connect(audioCtx.destination);
+    src.start();
   } else {
   } else {
-    wrongSound();
+    var o = audioCtx.createOscillator();
+    var g2 = audioCtx.createGain();
+    o.type = instr.wave;
+    o.frequency.value = instr.freq;
+    g2.gain.setValueAtTime(0, audioCtx.currentTime);
+    g2.gain.linearRampToValueAtTime(0.2, audioCtx.currentTime + 0.03);
+    g2.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + 0.7);
+    o.connect(g2); g2.connect(audioCtx.destination);
+    o.start();
+    o.stop(audioCtx.currentTime + 0.8);
   }
   }
-});
+}
+
+// 🎵 Победная мелодия
+function playMelody(){
+  if(!soundOn) return;
+  ensureAudio();
+  if(!audioCtx) return;
+  var seq = track.map(function(k){ return INSTRUMENTS[k].freq; });
+  seq.forEach(function(f, i){
+    setTimeout(function(){
+      var o = audioCtx.createOscillator();
+      var g = audioCtx.createGain();
+      o.type = 'triangle';
+      o.frequency.value = f;
+      g.gain.setValueAtTime(0, audioCtx.currentTime);
+      g.gain.linearRampToValueAtTime(0.2, audioCtx.currentTime + 0.02);
+      g.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + 0.5);
+      o.connect(g); g.connect(audioCtx.destination);
+      o.start();
+      o.stop(audioCtx.currentTime + 0.55);
+    }, i * 220);
+  });
+}
 
 
-// 🔁 Основной цикл анимации
+// 🔁 Основной цикл — РИСУЕМ ОБЕ ВОЛНЫ
 function drawType(){
 function drawType(){
   var W = typeCanvas.width, H = typeCanvas.height;
   var W = typeCanvas.width, H = typeCanvas.height;
   typeCtx.clearRect(0,0,W,H);
   typeCtx.clearRect(0,0,W,H);
+  typeT += 0.04;
 
 
-  if(waveType === 'long'){
-    drawLongitudinal(W, H);
-  } else {
-    drawTransverse(W, H);
-  }
-  requestAnimationFrame(drawType);
-}
+  // ============== ВЕРХНЯЯ ПОЛОВИНА: ПРОДОЛЬНАЯ ==============
+  var rowLong = H * 0.30;   // центр продольной волны (30% высоты)
+  drawLongitudinal(W, rowLong);
 
 
-// 🌊 ПРОДОЛЬНАЯ ВОЛНА — сжатия/разрежения + мини-игра
-function drawLongitudinal(W, H){
-  var spacing = W / (WAVE_N + 1);
+  // Разделитель
+  typeCtx.strokeStyle = 'rgba(255,255,255,.08)';
+  typeCtx.setLineDash([6,8]);
+  typeCtx.lineWidth = 1;
+  typeCtx.beginPath();
+  typeCtx.moveTo(20, H / 2);
+  typeCtx.lineTo(W - 20, H / 2);
+  typeCtx.stroke();
+  typeCtx.setLineDash([]);
 
 
-  // Если игра активна — фаза бежит. Иначе — статичная картинка.
-  if(gameActive){
-    gamePhase += gameSpeed;
-  }
+  // ============== НИЖНЯЯ ПОЛОВИНА: ПОПЕРЕЧНАЯ ==============
+  var rowTrans = H * 0.72;  // центр поперечной волны (72% высоты)
+  drawTransverse(W, rowTrans);
 
 
-  // Фоновый "коридор" среды
-  typeCtx.fillStyle = 'rgba(0,229,255,.04)';
-  typeCtx.fillRect(0, H/2 - 45, W, 90);
+  // Общие иконки "источник → ухо"
+  typeCtx.font = 'bold 18px Segoe UI';
+  typeCtx.fillStyle = 'rgba(57,255,20,.85)';
+  typeCtx.fillText('🔊', 4, rowLong + 6);
+  typeCtx.fillText('🔊', 4, rowTrans + 6);
+  typeCtx.fillStyle = 'rgba(0,229,255,.85)';
+  typeCtx.fillText('👂', W - 28, rowLong + 6);
+  typeCtx.fillText('👂', W - 28, rowTrans + 6);
 
 
-  // Определяем зоны сжатий (там, где sin > 0.7)
-  gameZones = [];
+  requestAnimationFrame(drawType);
+}
 
 
-  // Рисуем молекулы
-  for(var i=0; i<WAVE_N; i++){
-    var baseX = spacing * (i + 1);
+// 🌊 ВЕРХНЯЯ: продольная волна — сжатия/разрежения
+function drawLongitudinal(W, y0){
+  var N = 55;
+  var spacing = W / (N + 1);
 
 
-    // Смещение молекул: колеблются вокруг своей позиции.
-    // Формула: shift = sin(phase - i * k) — классическая продольная волна.
-    var k = (2 * Math.PI * WAVE_LEN) / WAVE_N;
-    var shift = Math.sin(gamePhase - i * k) * 14;
+  // Фоновый "коридор"
+  typeCtx.fillStyle = 'rgba(255,46,196,.04)';
+  typeCtx.fillRect(30, y0 - 30, W - 60, 60);
 
 
+  for(var i=0;i<N;i++){
+    var baseX = spacing * (i + 1);
+    var k = (2 * Math.PI * 4) / N;
+    var shift = Math.sin(typeT - i * k) * 11;
     var x = baseX + shift;
     var x = baseX + shift;
-
-    // Плотность: сжатие / разрежение
-    var density = 0.5 + 0.5 * Math.sin(gamePhase - i * k);  // 0..1
-    var r = 4 + density * 4;                                 // сжатие = крупнее
+    var density = 0.5 + 0.5 * Math.sin(typeT - i * k);
+    var r = 3.5 + density * 3.5;
     var alpha = 0.35 + density * 0.65;
     var alpha = 0.35 + density * 0.65;
 
 
     typeCtx.beginPath();
     typeCtx.beginPath();
     typeCtx.fillStyle = 'rgba(255,46,196,' + alpha + ')';
     typeCtx.fillStyle = 'rgba(255,46,196,' + alpha + ')';
-    typeCtx.shadowBlur = 12; typeCtx.shadowColor = '#ff2ec4';
-    typeCtx.arc(x, H/2, r, 0, Math.PI*2);
+    typeCtx.shadowBlur = 10; typeCtx.shadowColor = '#ff2ec4';
+    typeCtx.arc(x, y0, r, 0, Math.PI*2);
     typeCtx.fill();
     typeCtx.fill();
-
-    // Запоминаем позиции сжатий (для игры)
-    if(density > 0.85){
-      gameZones.push({ x: x, w: spacing * 1.6 });
-    }
-  }
-  typeCtx.shadowBlur = 0;
-
-  // 🎯 Подсветка зон сжатий (пульсирующий кружок)
-  if(gameActive){
-    for(var g=0; g<gameZones.length; g++){
-      var z = gameZones[g];
-      var pulse = 1 + 0.3 * Math.sin(typeT * 6);
-      typeCtx.beginPath();
-      typeCtx.strokeStyle = 'rgba(57,255,20,.6)';
-      typeCtx.lineWidth = 2;
-      typeCtx.shadowBlur = 15; typeCtx.shadowColor = '#39ff14';
-      typeCtx.arc(z.x, H/2, 22 * pulse, 0, Math.PI*2);
-      typeCtx.stroke();
-    }
-    typeCtx.shadowBlur = 0;
-  }
-
-  // ✨ Вспышки от успешных кликов
-  for(var b=gameBursts.length - 1; b >= 0; b--){
-    var burst = gameBursts[b];
-    typeCtx.beginPath();
-    typeCtx.strokeStyle = 'rgba(57,255,20,' + burst.alpha + ')';
-    typeCtx.lineWidth = 3;
-    typeCtx.shadowBlur = 25; typeCtx.shadowColor = '#39ff14';
-    typeCtx.arc(burst.x, burst.y, burst.r, 0, Math.PI*2);
-    typeCtx.stroke();
-    burst.r += 4;
-    burst.alpha -= 0.05;
-    if(burst.alpha <= 0) gameBursts.splice(b, 1);
   }
   }
   typeCtx.shadowBlur = 0;
   typeCtx.shadowBlur = 0;
 
 
-  // 🎚️ ПОРШЕНЬ слева
-  typeCtx.fillStyle = '#39ff14';
-  typeCtx.shadowBlur = 20; typeCtx.shadowColor = '#39ff14';
-  typeCtx.fillRect(10, H/2 - 30, 22, 60);
-  typeCtx.shadowBlur = 0;
-  typeCtx.fillStyle = '#39ff14';
-  typeCtx.font = 'bold 12px Segoe UI';
-  typeCtx.fillText('🔊', 14, H/2 + 5);
-
-  // 👂 УХО справа
-  typeCtx.fillStyle = '#00e5ff';
-  typeCtx.font = 'bold 22px Segoe UI';
-  typeCtx.fillText('👂', W - 35, H/2 + 8);
-
-  // Подписи
-  typeCtx.fillStyle = '#00e5ff';
-  typeCtx.font = 'bold 14px Segoe UI';
-  typeCtx.fillText('Продольная (звук в воздухе)', 50, 25);
-
-  // Стрелка направления волны
-  typeCtx.strokeStyle = '#39ff14';
-  typeCtx.lineWidth = 2;
-  typeCtx.beginPath();
-  typeCtx.moveTo(W - 140, 25);
-  typeCtx.lineTo(W - 45, 25);
-  typeCtx.lineTo(W - 60, 18);
-  typeCtx.moveTo(W - 45, 25);
-  typeCtx.lineTo(W - 60, 32);
-  typeCtx.stroke();
-
-  typeT += 0.03;
+  // Подпись
+  typeCtx.fillStyle = '#ff2ec4';
+  typeCtx.font = 'bold 13px Segoe UI';
+  typeCtx.fillText('🔴 Продольная (звук в воздухе)', 50, y0 - 42);
 }
 }
 
 
-// 🌊 ПОПЕРЕЧНАЯ ВОЛНА — синусоида, ширина зависит от периода
-function drawTransverse(W, H){
+// 🌊 НИЖНЯЯ: поперечная волна — синусоида (или волна выбранного инструмента)
+function drawTransverse(W, y0){
   // Ось
   // Ось
-  typeCtx.strokeStyle = 'rgba(0,229,255,.15)';
+  typeCtx.strokeStyle = 'rgba(0,229,255,.12)';
   typeCtx.lineWidth = 1;
   typeCtx.lineWidth = 1;
   typeCtx.beginPath();
   typeCtx.beginPath();
-  typeCtx.moveTo(0, H/2);
-  typeCtx.lineTo(W, H/2);
+  typeCtx.moveTo(30, y0);
+  typeCtx.lineTo(W - 30, y0);
   typeCtx.stroke();
   typeCtx.stroke();
 
 
-  // 4 гребня на экране
-  var wavesAcross = 4;
-  var pixelAmp = 55;
+  var amp = 40;
 
 
-  // Пунктирные границы
-  typeCtx.strokeStyle = 'rgba(255,46,196,.2)';
-  typeCtx.setLineDash([4,4]);
-  typeCtx.lineWidth = 1;
-  typeCtx.beginPath();
-  typeCtx.moveTo(0, H/2 - pixelAmp);  typeCtx.lineTo(W, H/2 - pixelAmp);
-  typeCtx.moveTo(0, H/2 + pixelAmp);  typeCtx.lineTo(W, H/2 + pixelAmp);
-  typeCtx.stroke();
-  typeCtx.setLineDash([]);
-
-  // Сама волна
-  typeCtx.strokeStyle = '#00e5ff';
-  typeCtx.lineWidth = 2;
-  typeCtx.shadowBlur = 15; typeCtx.shadowColor = '#00e5ff';
-  typeCtx.beginPath();
-  for(var x=0; x<W; x+=3){
-    var phaseX = (x / W) * Math.PI * 2 * wavesAcross;
-    var y = H/2 + Math.sin(phaseX + typeT * 0.8) * pixelAmp;
-    if(x===0) typeCtx.moveTo(x,y); else typeCtx.lineTo(x,y);
-  }
-  typeCtx.stroke();
-  typeCtx.shadowBlur = 0;
+  // Если выбран инструмент — рисуем ЕГО волну
+  if(currentInstr && INSTRUMENTS[currentInstr]){
+    var instr = INSTRUMENTS[currentInstr];
+    typeCtx.strokeStyle = instr.color;
+    typeCtx.lineWidth = 2.5;
+    typeCtx.shadowBlur = 18; typeCtx.shadowColor = instr.color;
+    typeCtx.beginPath();
+    for(var x=30; x<W-30; x+=3){
+      var phase = (x / W) * Math.PI * 2 * 5 + typeT * 2;
+      var y;
+      if(instr.wave === 'sine'){
+        y = y0 + Math.sin(phase) * amp;
+      } else if(instr.wave === 'square'){
+        y = y0 + Math.sign(Math.sin(phase)) * amp * 0.8;
+      } else {
+        y = y0 + (Math.random()*2 - 1) * amp * 0.85;
+      }
+      if(x===30) typeCtx.moveTo(x,y); else typeCtx.lineTo(x,y);
+    }
+    typeCtx.stroke();
+    typeCtx.shadowBlur = 0;
 
 
-  // Частицы на гребнях
-  var dotsCount = Math.round(wavesAcross * 6);
-  for(var k=0;k<dotsCount;k++){
-    var xx = (k + 0.5) * (W / dotsCount);
-    var phaseXDot = (xx / W) * Math.PI * 2 * wavesAcross;
-    var yy = H/2 + Math.sin(phaseXDot + typeT * 0.8) * pixelAmp;
+    typeCtx.fillStyle = instr.color;
+    typeCtx.font = 'bold 13px Segoe UI';
+    typeCtx.fillText(instr.emoji + ' ' + instr.name + ' · ' + instr.freq + ' Гц', 50, y0 - 42);
+  } else {
+    // Обычная синусоида
+    typeCtx.strokeStyle = '#00e5ff';
+    typeCtx.lineWidth = 2;
+    typeCtx.shadowBlur = 15; typeCtx.shadowColor = '#00e5ff';
     typeCtx.beginPath();
     typeCtx.beginPath();
-    typeCtx.fillStyle = '#ff2ec4';
-    typeCtx.shadowBlur = 15; typeCtx.shadowColor = '#ff2ec4';
-    typeCtx.arc(xx, yy, 6, 0, Math.PI*2);
-    typeCtx.fill();
-  }
-  typeCtx.shadowBlur = 0;
+    for(var x2=30; x2<W-30; x2+=3){
+      var y2 = y0 + Math.sin((x2/W)*Math.PI*2*5 + typeT * 2) * amp;
+      if(x2===30) typeCtx.moveTo(x2,y2); else typeCtx.lineTo(x2,y2);
+    }
+    typeCtx.stroke();
+    typeCtx.shadowBlur = 0;
 
 
-  typeCtx.fillStyle = '#00e5ff';
-  typeCtx.font = 'bold 14px Segoe UI';
-  typeCtx.fillText('Поперечная (волны на воде, струна)', 20, 30);
+    typeCtx.fillStyle = '#00e5ff';
+    typeCtx.font = 'bold 13px Segoe UI';
+    typeCtx.fillText('🟢 Поперечная (струна, волна на воде)', 50, y0 - 42);
+  }
 }
 }
 
 
-// ▶️ Кнопка Далее
+// ▶️ Далее
 document.getElementById('btnNext5').addEventListener('click', function(){ goToScene(5); });
 document.getElementById('btnNext5').addEventListener('click', function(){ goToScene(5); });
 
 
-// Запуск цикла
+// Запуск
 drawType();
 drawType();
 
 
 /* ================= SCENE 6: карточки + пары ================= */
 /* ================= SCENE 6: карточки + пары ================= */