NanoMaestro-Realtime / index.html
utkucoban's picture
Upload 3 files
709e20e verified
Raw
History Blame
10.1 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>
</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>
</header>
<div class="dashboard-content">
<!-- Left Sidebar / Info Panel -->
<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>
</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>
</div>
<!-- Seeding options -->
<div class="control-group seeding-group">
<div class="toggle-group" id="seedSourceGroup">
<button type="button" class="toggle-btn active" data-value="default">Start from Blank</button>
<button type="button" class="toggle-btn" data-value="midi">Start from MIDI</button>
</div>
</div>
</div>
<!-- MIDI Seeding Controls (Expandable under Main Row 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>
</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>
</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>