<html lang="de"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <title>Misa Kitara Mobile - Digital Synth <style>  :root {  --bg-color: #08080c;  --panel-bg: #12121c;  --accent-cyan: #00f0ff;  --accent-magenta: #ff0055;  --accent-gold: #ffcc00;  --grid-line: #222233;  }   * {  box-sizing: border-box;  user-select: none;  -webkit-user-select: none;  touch-action: none;  }   body {  background-color: var(--bg-color);  color: #fff;  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, monospace;  margin: 0;  padding: 8px;  height: 100vh;  height: 100dvh;  display: flex;  flex-direction: column;  gap: 8px;  overflow: hidden;  }   /* Top Header & Display */  .header {  background: var(--panel-bg);  border: 1px solid #333;  border-radius: 8px;  padding: 6px 10px;  display: flex;  justify-content: space-between;  align-items: center;  }   .title {  font-size: 0.9rem;  font-weight: 900;  color: var(--accent-cyan);  letter-spacing: 1px;  }   .status {  font-size: 0.7rem;  color: var(--accent-gold);  font-family: monospace;  }   /* Fretboard Matrix Area */  .fretboard-container {  background: var(--panel-bg);  border: 1px solid #333;  border-radius: 8px;  padding: 6px;  display: flex;  flex-direction: column;  gap: 4px;  flex-shrink: 0;  }   .string-row {  display: flex;  align-items: center;  gap: 4px;  height: 28px;  }   .string-label {  width: 24px;  font-size: 0.65rem;  font-weight: bold;  color: var(--accent-cyan);  text-align: center;  }   .fret-buttons {  display: flex;  flex: 1;  gap: 3px;  height: 100%;  }   .fret-btn {  flex: 1;  background: #1a1a26;  border: 1px solid #333;  color: #888;  font-size: 0.65rem;  font-weight: bold;  border-radius: 4px;  display: flex;  align-items: center;  justify-content: center;  cursor: pointer;  }   .fret-btn.active {  background: var(--accent-magenta);  color: #fff;  border-color: #ff6699;  box-shadow: 0 0 8px rgba(255, 0, 85, 0.6);  }   /* Kitara XY Performance Touchpad */  .pad-container {  flex: 1;  position: relative;  background: #040406;  border: 2px solid var(--accent-cyan);  border-radius: 10px;  overflow: hidden;  box-shadow: inset 0 0 20px rgba(0, 240, 255, 0.15);  }   canvas {  width: 100%;  height: 100%;  display: block;  }   /* FX Preset Control Bar */  .controls-bar {  display: flex;  gap: 6px;  height: 36px;  }   select, button {  background: #1a1a26;  color: #fff;  border: 1px solid #444;  border-radius: 6px;  padding: 4px 8px;  font-size: 0.75rem;  font-weight: bold;  flex: 1;  }   button.active {  background: var(--accent-cyan);  color: #000;  }   <body> <div class="header"> <div class="title">KITARA-MOBILE <div class="status" id="status-display">TOUCH PAD TO PLAY <div class="fretboard-container" id="fretboard"> <div class="pad-container" id="pad-box"> <canvas id="pad-canvas"> <div class="controls-bar"> <select id="synth-sound"> <option value="lead">Kitara Cyber Lead <option value="acid">303 Distorted <option value="poly">Digital PolySaw <option value="sub">Sub Bass Heavy <button id="delay-btn">DELAY: ON <script>  // Base Frequencies for 6 Standard Guitar Strings (E2, A2, D3, G3, B3, E4)  const baseNotes = [40, 45, 50, 55, 59, 64];   const availableFrets = [0, 1, 3, 5, 7, 8, 10, 12];  const stringStates = [0, 0, 0, 0, 0, 0]; // Selected fret per string   let audioCtx = null;  let masterGain, filterNode, driveNode, delayNode, delayFeedback;  let delayActive = true;  let activeVoices = {}; // Tracks active touch sound voices   function midiToFreq(midi) {  return 440 * Math.pow(2, (midi - 69) / 12);  }   // Audio Engine Setup  function initAudio() {  if (audioCtx) return;  audioCtx = new (window.AudioContext || window.webkitAudioContext)({ latencyHint: 'interactive' });   masterGain = audioCtx.createGain();  masterGain.gain.value = 0.4;   filterNode = audioCtx.createBiquadFilter();  filterNode.type = 'lowpass';  filterNode.frequency.value = 2500;  filterNode.Q.value = 4;   driveNode = audioCtx.createWaveShaper();  driveNode.curve = makeDistortionCurve(60);   // Delay FX  delayNode = audioCtx.createDelay();  delayNode.delayTime.value = 0.25; // 1/8 note vibe  delayFeedback = audioCtx.createGain();  delayFeedback.gain.value = 0.4;   delayNode.connect(delayFeedback);  delayFeedback.connect(delayNode);   // Routing  filterNode.connect(driveNode);  driveNode.connect(masterGain);  driveNode.connect(delayNode);  delayNode.connect(masterGain);   masterGain.connect(audioCtx.destination);  }   function makeDistortionCurve(amount) {  let k = amount, n = 44100, curve = new Float32Array(n), deg = Math.PI / 180;  for (let i = 0; i < n; ++i) {  let x = i * 2 / n - 1;  curve[i] = (3 + k) * x * 20 * deg / (Math.PI + k * Math.abs(x));  }  return curve;  }   // Play Kitara Voice on String Trigger  function startVoice(touchId, stringIndex, xRatio, yRatio) {  initAudio();  if (audioCtx.state === 'suspended') audioCtx.resume();   const midiNote = baseNotes[stringIndex] + stringStates[stringIndex];  const freq = midiToFreq(midiNote);  const preset = document.getElementById('synth-sound').value;   const osc1 = audioCtx.createOscillator();  const osc2 = audioCtx.createOscillator();  const voiceGain = audioCtx.createGain();   if (preset === 'acid') {  osc1.type = 'sawtooth'; osc2.type = 'sawtooth';  osc2.detune.value = 12;  } else if (preset === 'poly') {  osc1.type = 'sawtooth'; osc2.type = 'square';  osc2.detune.value = -7;  } else if (preset === 'sub') {  osc1.type = 'triangle'; osc2.type = 'sine';  } else { // Kitara Lead  osc1.type = 'sawtooth'; osc2.type = 'square';  osc2.detune.value = 5;  }   osc1.frequency.setValueAtTime(freq, audioCtx.currentTime);  osc2.frequency.setValueAtTime(freq, audioCtx.currentTime);   osc1.connect(voiceGain);  osc2.connect(voiceGain);  voiceGain.connect(filterNode);   // Fast Attack Envelope  voiceGain.gain.setValueAtTime(0.01, audioCtx.currentTime);  voiceGain.gain.linearRampToValueAtTime(0.8, audioCtx.currentTime + 0.01);   osc1.start(); osc2.start();   activeVoices[touchId] = { osc1, osc2, voiceGain, stringIndex };  updateSynthParameters(xRatio, yRatio);  }   function updateVoice(touchId, xRatio, yRatio, stringIndex) {  if (!activeVoices[touchId]) return;   // Pitch shift if finger slides to another string zone  if (activeVoices[touchId].stringIndex !== stringIndex) {  const midiNote = baseNotes[stringIndex] + stringStates[stringIndex];  const freq = midiToFreq(midiNote);  const now = audioCtx.currentTime;  activeVoices[touchId].osc1.frequency.setTargetAtTime(freq, now, 0.01);  activeVoices[touchId].osc2.frequency.setTargetAtTime(freq, now, 0.01);  activeVoices[touchId].stringIndex = stringIndex;  }   updateSynthParameters(xRatio, yRatio);  }   function stopVoice(touchId) {  if (!activeVoices[touchId]) return;  const { osc1, osc2, voiceGain } = activeVoices[touchId];  const now = audioCtx.currentTime;   voiceGain.gain.cancelScheduledValues(now);  voiceGain.gain.setTargetAtTime(0.001, now, 0.05);   setTimeout(() => {  try { osc1.stop(); osc2.stop(); osc1.disconnect(); osc2.disconnect(); } catch(e){}  }, 100);   delete activeVoices[touchId];  }   function updateSynthParameters(xRatio, yRatio) {  if (!audioCtx) return;  const now = audioCtx.currentTime;   // X-Axis controls Filter Cutoff (100 Hz - 8000 Hz)  const cutoff = 100 * Math.pow(80, xRatio);  filterNode.frequency.setTargetAtTime(cutoff, now, 0.01);   // Y-Axis controls Resonance & Distortion Gain  filterNode.Q.setTargetAtTime(2 + (yRatio * 16), now, 0.01);    document.getElementById('status-display').innerText =   `CUTOFF: ${Math.round(cutoff)}Hz | RES: ${Math.round(2 + yRatio * 16)}`;  }   // Render Fretboard UI  const fretboardEl = document.getElementById('fretboard');  const stringNames = ['E2', 'A2', 'D3', 'G3', 'B3', 'E4'];   stringNames.forEach((name, sIdx) => {  const row = document.createElement('div');  row.className = 'string-row';   const label = document.createElement('div');  label.className = 'string-label';  label.innerText = name[0] + name[1];  row.appendChild(label);   const fretContainer = document.createElement('div');  fretContainer.className = 'fret-buttons';   availableFrets.forEach((fret) => {  const btn = document.createElement('div');  btn.className = `fret-btn ${fret === 0 ? 'active' : ''}`;  btn.innerText = fret === 0 ? 'O' : fret;  btn.dataset.string = sIdx;  btn.dataset.fret = fret;   btn.addEventListener('pointerdown', (e) => {  e.preventDefault();  stringStates[sIdx] = fret;  row.querySelectorAll('.fret-btn').forEach(b => b.classList.remove('active'));  btn.classList.add('active');  });   fretContainer.appendChild(btn);  });   row.appendChild(fretContainer);  fretboardEl.appendChild(row);  });   // Touchpad & Canvas Performance Rendering  const padBox = document.getElementById('pad-box');  const canvas = document.getElementById('pad-canvas');  const ctx = canvas.getContext('2d');  let touches = [];   function resizeCanvas() {  canvas.width = padBox.clientWidth;  canvas.height = padBox.clientHeight;  }  window.addEventListener('resize', resizeCanvas);  resizeCanvas();   function getPadCoordinates(e) {  const rect = padBox.getBoundingClientRect();  const touchList = e.touches ? Array.from(e.touches) : [e];  return touchList.map(t => {  const x = Math.max(0, Math.min(rect.width, t.clientX - rect.left));  const y = Math.max(0, Math.min(rect.height, t.clientY - rect.top));  const xRatio = x / rect.width;  const yRatio = 1 - (y / rect.height);  // 6 Virtual String Zones on Y-Axis  const stringIndex = Math.min(5, Math.floor((y / rect.height) * 6));  return { id: t.identifier ?? 'mouse', x, y, xRatio, yRatio, stringIndex };  });  }   function handleTouchStart(e) {  e.preventDefault();  const currentTouches = getPadCoordinates(e);  currentTouches.forEach(t => {  startVoice(t.id, t.stringIndex, t.xRatio, t.yRatio);  });  touches = currentTouches;  }   function handleTouchMove(e) {  e.preventDefault();  const currentTouches = getPadCoordinates(e);  currentTouches.forEach(t => {  updateVoice(t.id, t.xRatio, t.yRatio, t.stringIndex);  });  touches = currentTouches;  }   function handleTouchEnd(e) {  e.preventDefault();  const remainingTouches = getPadCoordinates(e);  const remainingIds = new Set(remainingTouches.map(t => t.id));   Object.keys(activeVoices).forEach(id => {  if (!remainingIds.has(id === 'mouse' ? 'mouse' : Number(id))) {  stopVoice(id);  }  });  touches = remainingTouches;  }   padBox.addEventListener('touchstart', handleTouchStart, { passive: false });  padBox.addEventListener('touchmove', handleTouchMove, { passive: false });  padBox.addEventListener('touchend', handleTouchEnd, { passive: false });   let isMouseDown = false;  padBox.addEventListener('mousedown', (e) => { isMouseDown = true; handleTouchStart(e); });  window.addEventListener('mousemove', (e) => { if (isMouseDown) handleTouchMove(e); });  window.addEventListener('mouseup', (e) => { if (isMouseDown) { isMouseDown = false; handleTouchEnd(e); } });   // Visualizer Loop  function drawPad() {  requestAnimationFrame(drawPad);  ctx.fillStyle = '#040406';  ctx.fillRect(0, 0, canvas.width, canvas.height);   // Draw 6 Virtual String Bands (Kitara Style)  const bandHeight = canvas.height / 6;  ctx.lineWidth = 1;  for (let i = 0; i < 6; i++) {  ctx.strokeStyle = '#181828';  ctx.beginPath();  ctx.moveTo(0, i * bandHeight);  ctx.lineTo(canvas.width, i * bandHeight);  ctx.stroke();   // Highlight Active Fretted String Lines  ctx.strokeStyle = 'rgba(0, 240, 255, 0.15)';  ctx.beginPath();  ctx.moveTo(0, i * bandHeight + bandHeight / 2);  ctx.lineTo(canvas.width, i * bandHeight + bandHeight / 2);  ctx.stroke();  }   // Draw Touch Pointers  touches.forEach(t => {  // Horizontal / Vertical Crosshair  ctx.strokeStyle = 'rgba(255, 0, 85, 0.4)';  ctx.beginPath(); ctx.moveTo(t.x, 0); ctx.lineTo(t.x, canvas.height); ctx.stroke();  ctx.beginPath(); ctx.moveTo(0, t.y); ctx.lineTo(canvas.width, t.y); ctx.stroke();   // Glowing Touch Point  ctx.fillStyle = '#00f0ff';  ctx.shadowColor = '#00f0ff';  ctx.shadowBlur = 12;  ctx.beginPath();  ctx.arc(t.x, t.y, 16, 0, Math.PI * 2);  ctx.fill();  ctx.shadowBlur = 0;  });  }  drawPad();   // UI Listeners  document.getElementById('delay-btn').addEventListener('click', (e) => {  delayActive = !delayActive;  delayFeedback.gain.value = delayActive ? 0.4 : 0;  e.target.innerText = `DELAY: ${delayActive ? 'ON' : 'OFF'}`;  e.target.classList.toggle('active', delayActive);  });