| <!doctype html> |
| <html lang="en"> |
| <head> |
| <meta charset="utf-8" /> |
| <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" /> |
| <title>NanoMaestro Realtime - Real-time AI Music</title> |
| <link rel="preconnect" href="https://fonts.googleapis.com"> |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet"> |
| <link rel="stylesheet" href="./styles.css?v=2.2" /> |
| </head> |
| <body> |
| |
| <div id="loadingOverlay" class="loading-overlay"> |
| <div class="loader-card"> |
| <h1 class="loader-title">NanoMaestro Realtime</h1> |
| <p class="loader-subtitle">Real-time Lightweight Music Model</p> |
| |
| <div class="progress-container"> |
| <div class="progress-bar"> |
| <span id="loadProgress"></span> |
| </div> |
| <div class="progress-details"> |
| <span id="loadStatus">Preparing application...</span> |
| <span id="loadPercent">0%</span> |
| </div> |
| </div> |
|
|
| <div class="loader-cta"> |
| <a href="https://nanomaestro.app" target="_blank" rel="noopener" class="cta-link-badge"> |
| <span>Try the newest web app <span class="cta-extra-text">for the best experience and latest features</span>!</span> |
| <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg> |
| </a> |
| </div> |
|
|
| <div class="loader-heart-notice"> |
| <a href="https://huggingface.co/utkucoban/NanoMaestro-Realtime" target="_blank" rel="noopener"> |
| Don't forget to heart the model if you like it! ❤️ |
| </a> |
| </div> |
| </div> |
| </div> |
| |
| |
| <main class="dashboard-shell"> |
| |
| <header class="app-header"> |
| <div class="brand"> |
| <div> |
| <h2>NanoMaestro Realtime</h2> |
| <p id="appStatus">Ready to perform</p> |
| </div> |
| </div> |
| <a href="https://nanomaestro.app" target="_blank" rel="noopener" class="cta-link-badge"> |
| <span>Try the newest web app <span class="cta-extra-text">for the best experience and latest features</span>!</span> |
| <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg> |
| </a> |
| </header> |
| |
| <div class="dashboard-content"> |
| |
| <aside class="sidebar-spacer info-sidebar"> |
| <div class="info-card"> |
| <h5>About NanoMaestro</h5> |
| <p>This model is a tiny, approximately 50 MB music-generation model with about 13 million parameters, designed to run continuously in real time on almost any consumer CPU.</p> |
| <p>Inference runs entirely on your device through WebAssembly (WASM), allowing NanoMaestro to generate music directly on your device locally.</p> |
| <div class="heart-notice"> |
| <a href="https://huggingface.co/utkucoban/NanoMaestro-Realtime" target="_blank" rel="noopener"> |
| Don't forget to heart the model if you like it! ❤️ |
| </a> |
| </div> |
| </div> |
|
|
|
|
| |
| <div class="info-card"> |
| <h5>Bored with the song?</h5> |
| <p>Click the <strong>New Song</strong> button to generate a completely new unique song. NanoMaestro will keep generating forever!</p> |
| </div> |
| </aside> |
| |
| |
| <div class="main-column"> |
| |
| <section class="visualizer-panel"> |
| <div class="panel-header"> |
| <h3>Performance Visualizer</h3> |
| <div class="hud-stats"> |
| <div class="hud-item"> |
| <span class="hud-label">BPM</span> |
| <span class="hud-value" id="tempo">120</span> |
| </div> |
| <div class="hud-item"> |
| <span class="hud-label">Notes</span> |
| <span class="hud-value" id="eventCount">0</span> |
| </div> |
| <div class="hud-item"> |
| <span class="hud-label">Last Token</span> |
| <span class="hud-value" id="lastToken">-</span> |
| </div> |
| <div class="hud-item queue-hud-item"> |
| <span class="hud-label">Queue</span> |
| <div class="queue-hud-row"> |
| <div class="readout-progress"> |
| <span id="queueFill"></span> |
| </div> |
| <span class="hud-value" id="queueCount">0</span> |
| </div> |
| </div> |
| </div> |
| </div> |
| <div class="visualizer-container"> |
| <canvas id="noteCanvas"></canvas> |
| </div> |
| </section> |
| |
| |
| <section class="controls-panel-bottom"> |
| |
| <div class="controls-main-row"> |
| |
| <div class="control-group playback-actions"> |
| <button id="startBtn" type="button" class="btn-primary" disabled> |
| <svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M8 5v14l11-7z"/></svg> |
| Start Performance |
| </button> |
| <button id="stopBtn" type="button" class="btn-secondary" disabled> |
| <svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></svg> |
| Stop |
| </button> |
| <button id="newSongBtn" type="button" class="btn-secondary" disabled> |
| <svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/></svg> |
| New Song |
| </button> |
| <button id="recordBtn" type="button" class="btn-secondary" disabled> |
| <svg id="recordIcon" viewBox="0 0 24 24" width="20" height="20" fill="currentColor"> |
| <circle cx="12" cy="12" r="8" fill="#d9534f"/> |
| </svg> |
| <span id="recordBtnText">Record</span> |
| </button> |
| </div> |
| </div> |
| </section> |
| </div> |
|
|
| |
| <aside class="sidebar-column"> |
| |
| <details class="advanced-details settings-dropdown"> |
| <summary>Generation & Acoustic Settings</summary> |
| <div class="advanced-content settings-vertical-stack"> |
| |
| <div class="settings-column"> |
| <h5>Generation Engine</h5> |
| <div class="slider-group"> |
| <div class="slider-header"> |
| <label for="temperature">Creativity (Temp)</label> |
| <span id="temperatureValue">0.85</span> |
| </div> |
| <input id="temperature" type="range" min="0.45" max="1.35" step="0.05" value="0.85" /> |
| </div> |
|
|
| <div class="slider-group"> |
| <div class="slider-header"> |
| <label for="topK">Vocabulary Focus (Top K)</label> |
| <span id="topKValue">40</span> |
| </div> |
| <input id="topK" type="range" min="5" max="120" step="1" value="40" /> |
| </div> |
| </div> |
|
|
| |
| <div class="settings-column"> |
| <h5>Acoustic Parameters</h5> |
| <div class="slider-group"> |
| <div class="slider-header"> |
| <label for="masterVolume">Volume</label> |
| <span id="volumeValue">85%</span> |
| </div> |
| <input id="masterVolume" type="range" min="0" max="100" step="1" value="85" /> |
| </div> |
|
|
| <div class="slider-group"> |
| <div class="slider-header"> |
| <label for="reverbWet">Room Size (Reverb)</label> |
| <span id="reverbValue">22%</span> |
| </div> |
| <input id="reverbWet" type="range" min="0" max="100" step="1" value="22" /> |
| </div> |
|
|
| <div class="slider-group"> |
| <div class="slider-header"> |
| <label for="delayWet">Echo (Delay)</label> |
| <span id="delayValue">8%</span> |
| </div> |
| <input id="delayWet" type="range" min="0" max="100" step="1" value="8" /> |
| </div> |
|
|
| <div class="slider-group"> |
| <div class="slider-header"> |
| <label for="synthRelease">Sustain (Release)</label> |
| <span id="releaseValue">1.2s</span> |
| </div> |
| <input id="synthRelease" type="range" min="0.2" max="3.0" step="0.1" value="1.2" /> |
| </div> |
| </div> |
| </div> |
| </details> |
|
|
| |
| <div class="playing-style-panel settings-column"> |
| <h5>Playing Style</h5> |
| <div class="toggle-group" id="seedSourceGroup"> |
| <button type="button" class="toggle-btn" data-value="default">Start from Blank</button> |
| <button type="button" class="toggle-btn" data-value="midi">Start from MIDI</button> |
| <button type="button" class="toggle-btn" data-value="bach">Bach</button> |
| <button type="button" class="toggle-btn" data-value="mozart"> |
| <span>Mozart</span> |
| <span class="rec-tag">Recommended</span> |
| </button> |
| <button type="button" class="toggle-btn" data-value="chopin">Chopin</button> |
| <button type="button" class="toggle-btn" data-value="debussy">Debussy</button> |
| <button type="button" class="toggle-btn active" data-value="beethoven"> |
| <span>Beethoven</span> |
| <span class="rec-tag">Recommended</span> |
| </button> |
| <button type="button" class="toggle-btn" data-value="satie">Satie</button> |
| </div> |
| |
| |
| <div id="midiSeedControls" style="display: none;" class="midi-seed-controls"> |
| <div class="input-file-group"> |
| <label for="midiFileInput">Select MIDI File</label> |
| <input id="midiFileInput" type="file" accept=".mid,.midi" /> |
| </div> |
| |
| <div id="midiFileInfo" class="midi-file-info" style="display: none; margin-top: 10px;"> |
| <p id="midiFileName" class="midi-info-text" style="font-weight: 500;"></p> |
| <p id="midiFileStats" class="midi-info-text" style="margin-top: 2px;"></p> |
| |
| <div class="timeline-container" style="margin-top: 14px;"> |
| <span class="slider-header" style="font-size: 0.72rem; margin-bottom: 6px; color: var(--color-text-muted);">Visual Song Map & Warm-up Context (256 Tokens)</span> |
| <div id="midiTimeline" class="midi-timeline"></div> |
| </div> |
| |
| <div class="slider-group" style="margin-top: 12px;"> |
| <div class="slider-header"> |
| <label for="midiStartBar">Start Generation from Bar</label> |
| <span id="midiStartBarValue">0</span> |
| </div> |
| <input id="midiStartBar" type="range" min="0" max="0" step="1" value="0" /> |
| </div> |
| <p id="midiStartBarTime" class="midi-info-subtext"></p> |
| |
| <div class="toggle-group-container" style="margin-top: 14px;"> |
| <span class="toggle-group-label" style="font-size: 0.72rem; margin-bottom: 6px; display: block; color: var(--color-text-muted);">MIDI Playback Tempo Mode</span> |
| <div class="toggle-group" id="midiBpmModeGroup"> |
| <button type="button" class="toggle-btn active" data-value="lock">Lock MIDI BPM</button> |
| <button type="button" class="toggle-btn" data-value="model">Follow Model BPM</button> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="playing-style-panel settings-column" style="margin-top: 15px;"> |
| <h5>Playback Instrument</h5> |
| <div class="custom-select-container"> |
| <select id="instrumentSelect"> |
| <option value="piano" selected>Piano (Default)</option> |
| <option value="xylophone">Xylophone</option> |
| <option value="violin">Violin (Recommended)</option> |
| <option value="trumpet">Trumpet (Recommended)</option> |
| <option value="saxophone">Saxophone</option> |
| <option value="organ">Organ (Recommended)</option> |
| <option value="harp">Harp</option> |
| <option value="guitar-acoustic">Acoustic Guitar</option> |
| <option value="guitar-electric">Electric Guitar</option> |
| <option value="bass-electric">Electric Bass</option> |
| </select> |
| </div> |
| </div> |
| </aside> |
| </div> |
| </main> |
|
|
| <script src="https://cdn.jsdelivr.net/npm/onnxruntime-web@1.20.1/dist/ort.min.js"></script> |
| <script src="https://cdn.jsdelivr.net/npm/tone@15.1.22/build/Tone.js"></script> |
| <script type="module" src="./app.js?v=2.2"></script> |
| </body> |
| </html> |
|
|