NanoMaestro-Realtime / index.html
utkucoban's picture
Upload 4 files
fc78499 verified
Raw
History Blame Contribute Delete
15.2 kB
<!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>
<!-- Full-screen Loading Overlay -->
<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 Application Shell -->
<main class="dashboard-shell">
<!-- Topbar Header -->
<header class="app-header">
<div class="brand">
<div>
<h2>NanoMaestro Realtime</h2>
<p id="appStatus">Ready to perform</p>
</div>
</div>
<!-- Top Model Switcher -->
<div class="top-model-selector">
<label for="topModelSelect" class="top-model-label">Model Selector</label>
<div class="custom-select-container top-select-container">
<select id="topModelSelect">
<option value="pro" selected>NanoMaestro Pro (54M - INT8 CPU)</option>
<option value="light">NanoMaestro Light (13M - INT8 CPU)</option>
</select>
</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">
<!-- Left Sidebar / Info Panel -->
<aside class="sidebar-spacer info-sidebar">
<div class="info-card">
<h5>About NanoMaestro</h5>
<p id="modelDesc">NanoMaestro Pro is a 50 MB music-generation model with 54 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>
<!-- Left Column: Visualizer and bottom controls -->
<div class="main-column">
<!-- Visualizer Area (Main Panel) -->
<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>
<!-- Control Section Below Visualizer -->
<section class="controls-panel-bottom">
<!-- Playback Actions & Seeding (Main Row) -->
<div class="controls-main-row">
<!-- Playback Actions -->
<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>
<!-- Right Column: Settings Dropdown -->
<aside class="sidebar-column">
<!-- Settings Dropdown (Collapsible details element covering all sliders) -->
<details class="advanced-details settings-dropdown">
<summary>Generation & Acoustic Settings</summary>
<div class="advanced-content settings-vertical-stack">
<!-- Generation Engine Settings -->
<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>
<!-- Acoustic Settings -->
<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>
<!-- Playing Style Box -->
<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>
<!-- MIDI Seeding Controls (Expandable if active) -->
<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>
<!-- Playback Instrument Box -->
<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>