JSFiddle - React, Tailwind, and code Playground

by gustav75

HTML

<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Virtuelles Klavier</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div id="piano">
        <div class="key white" data-note="C"></div>
        <div class="key black" data-note="C#" style="left: 50px;"></div>
        <div class="key white" data-note="D"></div>
        <div class="key black" data-note="D#" style="left: 130px;"></div>
        <div class="key white" data-note="E"></div>
        <div class="key white" data-note="F"></div>
        <div class="key black" data-note="F#" style="left: 290px;"></div>
        <div class="key white" data-note="G"></div>
        <div class="key black" data-note="G#" style="left: 370px;"></div>
        <div class="key white" data-note="A"></div>
        <div class="key black" data-note="A#" style="left: 450px;"></div>
        <div class="key white" data-note="B"></div>
        <div class="key white" data-note="C2"></div>
    </div>
    <script src="script.js"></script>
</body>
</html>

CSS

body {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    margin: 0;
    background-color: #333;
}

#piano {
    position: relative;
    width: 760px;
}

.key {
    width: 80px;
    height: 300px;
    border: 1px solid #000;
    box-sizing: border-box;
    position: relative;
    cursor: pointer;
}

.white {
    background-color: white;
    z-index: 1;
    float: left;
}

.black {
    background-color: black;
    width: 60px;
    height: 180px;
    position: absolute;
    top: 0;
    z-index: 2;
}

JavaScript

const notes = {
    'C': 'https://lagustav.ch/wp-content/uploads/2024/08/C.mp3',
    'C#': 'https://lagustav.ch/wp-content/uploads/2024/08/C-1.mp3',
    'D': 'https://lagustav.ch/wp-content/uploads/2024/08/D.mp3',
    'D#': 'https://lagustav.ch/wp-content/uploads/2024/08/D-1.mp3',
    'E': 'https://lagustav.ch/wp-content/uploads/2024/08/E.mp3',
    'F': 'https://lagustav.ch/wp-content/uploads/2024/08/F.mp3',
    'F#': 'https://lagustav.ch/wp-content/uploads/2024/08/F-1.mp3',
    'G': 'https://lagustav.ch/wp-content/uploads/2024/08/G.mp3',
    'G#': 'https://lagustav.ch/wp-content/uploads/2024/08/G-1.mp3',
    'A': 'https://lagustav.ch/wp-content/uploads/2024/08/A.mp3',
    'A#': 'https://lagustav.ch/wp-content/uploads/2024/08/A-1.mp3',
    'B': 'https://lagustav.ch/wp-content/uploads/2024/08/H.mp3',
    'C2': 'https://lagustav.ch/wp-content/uploads/2024/08/C2.mp3'
};

document.querySelectorAll('.key').forEach(key => {
    key.addEventListener('click', () => {
        const note = key.dataset.note;
        const audio = new Audio(notes[note]);
        audio.play();
    });
});