ajidim (html, doesn't expire)
<!DOCTYPE html>
<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</title>
    <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;
        }
    </style>
</head>
<body>

    <div class="header">
        <div class="title">KITARA-MOBILE</div>
        <div class="status" id="status-display">TOUCH PAD TO PLAY</div>
    </div>

    <div class="fretboard-container" id="fretboard"></div>

    <div class="pad-container" id="pad-box">
        <canvas id="pad-canvas"></canvas>
    </div>

    <div class="controls-bar">
        <select id="synth-sound">
            <option value="lead">Kitara Cyber Lead</option>
            <option value="acid">303 Distorted</option>
            <option value="poly">Digital PolySaw</option>
            <option value="sub">Sub Bass Heavy</option>
        </select>
        <button id="delay-btn">DELAY: ON</button>
    </div>

<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);
    });
</script>
</body>
</html>