Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1" /> | |
| <title>Web Continuator</title> | |
| <link rel="stylesheet" href="/assets/styles.css?v=20260602-session-midi-zip3" /> | |
| </head> | |
| <body> | |
| <div class="page-aura"></div> | |
| <div class="page-grid"></div> | |
| <header class="hero"> | |
| <div class="hero-copy"> | |
| <h1>Web Continuator</h1> | |
| </div> | |
| <div class="hero-side"> | |
| <span id="timing-readout" class="hero-status small-copy">Waiting for first phrase</span> | |
| <div class="hero-actions"> | |
| <button | |
| class="hero-utility-button ghost" | |
| id="open-readme-button" | |
| type="button" | |
| aria-haspopup="dialog" | |
| aria-expanded="false" | |
| aria-controls="readme-panel" | |
| > | |
| Read Me | |
| </button> | |
| </div> | |
| <button | |
| class="account-trigger" | |
| id="auth-status" | |
| type="button" | |
| aria-expanded="false" | |
| aria-controls="account-panel" | |
| > | |
| <span id="auth-avatar" class="account-avatar" aria-hidden="true">G</span> | |
| <span class="account-trigger-copy"> | |
| <span id="auth-status-label" class="account-trigger-label">Guest mode</span> | |
| <span id="auth-status-copy" class="account-trigger-meta">Click to sign in</span> | |
| </span> | |
| </button> | |
| <section id="account-panel" class="account-panel hero-account-panel" hidden> | |
| <div class="section-head"> | |
| <div> | |
| <h3>Account</h3> | |
| <p id="account-panel-copy" class="section-copy"> | |
| Guest mode works immediately. Sign in only if you want your | |
| sessions saved and reopenable. | |
| </p> | |
| </div> | |
| </div> | |
| <dl class="info-grid"> | |
| <div> | |
| <dt>Current User</dt> | |
| <dd id="auth-user-name">Not signed in</dd> | |
| </div> | |
| <div> | |
| <dt>Saved Sessions</dt> | |
| <dd id="saved-session-count">Guest mode</dd> | |
| </div> | |
| </dl> | |
| <div id="auth-signed-out-panel"> | |
| <div class="auth-grid"> | |
| <div class="field"> | |
| <label for="auth-username-input">Username</label> | |
| <input | |
| id="auth-username-input" | |
| type="text" | |
| minlength="2" | |
| maxlength="32" | |
| autocomplete="username" | |
| placeholder="continuator_user" | |
| /> | |
| <p class="field-hint"> | |
| Use 2 to 32 letters, digits, underscores, dots, or dashes. | |
| </p> | |
| </div> | |
| <div class="field"> | |
| <label for="auth-password-input">Password</label> | |
| <input | |
| id="auth-password-input" | |
| type="password" | |
| minlength="8" | |
| maxlength="128" | |
| autocomplete="current-password" | |
| placeholder="At least 8 characters" | |
| /> | |
| <p class="field-hint">Passwords need at least 8 characters.</p> | |
| </div> | |
| </div> | |
| <div class="button-row"> | |
| <button id="login-button" type="button">Sign In</button> | |
| <button id="register-button" type="button" class="ghost"> | |
| Create Account | |
| </button> | |
| </div> | |
| </div> | |
| <div id="auth-signed-in-panel" class="button-row" hidden> | |
| <button | |
| id="account-open-sessions-button" | |
| type="button" | |
| class="ghost" | |
| > | |
| Open Saved Sessions | |
| </button> | |
| <button id="logout-button" type="button" class="ghost">Sign Out</button> | |
| </div> | |
| <div id="auth-message-box" class="message-box auth-message-box"> | |
| Guest mode is ready. Sign in only if you want saved sessions. | |
| </div> | |
| </section> | |
| </div> | |
| </header> | |
| <div id="readme-overlay" class="sheet-overlay" hidden></div> | |
| <aside | |
| id="readme-panel" | |
| class="readme-panel" | |
| role="dialog" | |
| aria-modal="true" | |
| aria-labelledby="readme-title" | |
| hidden | |
| > | |
| <div class="readme-head"> | |
| <div> | |
| <p class="readme-kicker">Read Me</p> | |
| <h2 id="readme-title">Web Continuator Guide</h2> | |
| <p class="section-copy"> | |
| A compact overview of the app, the musical flow, and how sessions | |
| are saved. | |
| </p> | |
| </div> | |
| <button id="close-readme-button" class="ghost sheet-close" type="button"> | |
| Close | |
| </button> | |
| </div> | |
| <div class="readme-sections"> | |
| <section class="readme-block"> | |
| <h3>What It Does</h3> | |
| <p> | |
| Web Continuator captures a phrase from browser MIDI, sends it to | |
| the Python Continuator engine, and plays back the continuation | |
| either through the built-in browser synth or a MIDI output port. | |
| </p> | |
| </section> | |
| <section class="readme-block"> | |
| <h3>Quick Start</h3> | |
| <ol class="readme-list"> | |
| <li>Create or open a session.</li> | |
| <li>Click <strong>Connect MIDI</strong> and choose an input.</li> | |
| <li>Play a short phrase on your keyboard.</li> | |
| <li>Wait for the phrase to end, or click <strong>Send Phrase</strong>.</li> | |
| <li>Review the continuation in the piano rolls, history, and memory.</li> | |
| </ol> | |
| </section> | |
| <section class="readme-block"> | |
| <h3>Saving Session MIDI</h3> | |
| <p> | |
| The Session panel can export the current session as a MIDI ZIP | |
| archive. The archive contains the played input phrases and generated | |
| continuations in session order. | |
| </p> | |
| <p> | |
| In Chrome and Edge, <strong>Don't ask again</strong> remembers the | |
| selected ZIP file and overwrites it on future saves. Other browsers | |
| and embedded Hugging Face pages use the normal download flow. | |
| </p> | |
| </section> | |
| <section class="readme-block"> | |
| <h3>Studio Layout</h3> | |
| <ul class="readme-list"> | |
| <li><strong>Perform</strong>: live MIDI capture, playback routing, phrase generation, replay, and infinite mode.</li> | |
| <li><strong>Memory</strong>: corpus import, active engine memory, and phrase history.</li> | |
| <li><strong>Session</strong>: session lifecycle, advanced Continuator settings, and saved sessions.</li> | |
| </ul> | |
| </section> | |
| <section class="readme-block"> | |
| <h3>History vs Memory</h3> | |
| <p> | |
| <strong>History</strong> is the logged record of phrases sent and | |
| generated for a session. <strong>Memory</strong> is the live state | |
| currently active inside the Continuator engine. They are related, | |
| but not always identical. | |
| </p> | |
| </section> | |
| <section class="readme-block"> | |
| <h3>Accounts and Saved Sessions</h3> | |
| <p> | |
| Guest mode works immediately. Sign in only if you want sessions | |
| saved under your username so you can reopen them later. The account | |
| menu lives in the top-right header and includes sign in, sign out, | |
| and saved-session access. | |
| </p> | |
| </section> | |
| <section class="readme-block"> | |
| <h3>History and References</h3> | |
| <p> | |
| Continuator was developed by François Pachet at Sony CSL Paris in | |
| 1999 as an interactive musical system that learns a | |
| performer’s style in real time and answers with stylistically | |
| coherent continuations. The foundational paper introduced the idea | |
| of a playable “virtual musician”, later articles described the | |
| instrument in practice and in educational settings, and the current | |
| Python implementation extends the approach with more explicit | |
| controllable constraints. | |
| </p> | |
| <ul class="readme-list readme-link-list"> | |
| <li> | |
| <a | |
| href="https://github.com/fpachet/Continuator" | |
| target="_blank" | |
| rel="noreferrer noopener" | |
| > | |
| GitHub: fpachet/Continuator | |
| </a> | |
| </li> | |
| <li> | |
| <a | |
| href="https://doi.org/10.1076/jnmr.32.3.333.16861" | |
| target="_blank" | |
| rel="noreferrer noopener" | |
| > | |
| The Continuator: Musical Interaction With Style (2003) | |
| </a> | |
| </li> | |
| <li> | |
| <a | |
| href="https://doi.org/10.1109/MCG.2004.1255806" | |
| target="_blank" | |
| rel="noreferrer noopener" | |
| > | |
| Beyond the Cybernetic Jam Fantasy: the Continuator (2004) | |
| </a> | |
| </li> | |
| <li> | |
| <a | |
| href="https://doi.org/10.1177/1029864906010001021" | |
| target="_blank" | |
| rel="noreferrer noopener" | |
| > | |
| Young Children Confronting the Continuator (2006) | |
| </a> | |
| </li> | |
| <li> | |
| <a | |
| href="https://www.francoispachet.fr/tag/ai-music/" | |
| target="_blank" | |
| rel="noreferrer noopener" | |
| > | |
| François Pachet publications and AI music notes | |
| </a> | |
| </li> | |
| </ul> | |
| </section> | |
| <section class="readme-block"> | |
| <h3>Performance Notes</h3> | |
| <ul class="readme-list"> | |
| <li><strong>Auto-send phrase</strong> starts generation after the phrase ends.</li> | |
| <li>A phrase closes after the selected phrase gap following the final note-off.</li> | |
| <li><strong>Infinite mode</strong> keeps chaining continuations until you stop it.</li> | |
| <li>Playing live MIDI interrupts whatever continuation is currently playing.</li> | |
| </ul> | |
| </section> | |
| </div> | |
| </aside> | |
| <div id="graph-overlay" class="sheet-overlay" hidden></div> | |
| <aside | |
| id="graph-panel" | |
| class="graph-panel" | |
| role="dialog" | |
| aria-modal="false" | |
| aria-labelledby="graph-title" | |
| hidden | |
| > | |
| <div class="graph-head"> | |
| <div> | |
| <p class="readme-kicker">State Graph</p> | |
| <h2 id="graph-title">Continuator Graph</h2> | |
| <p id="graph-meta" class="section-copy"> | |
| Snapshot of the active session memory. | |
| </p> | |
| </div> | |
| <button id="close-graph-button" class="ghost sheet-close" type="button"> | |
| Close | |
| </button> | |
| </div> | |
| <div class="graph-tabs" role="tablist" aria-label="Continuator graph views"> | |
| <button | |
| class="graph-tab is-active" | |
| data-graph-kind="memory" | |
| type="button" | |
| role="tab" | |
| aria-selected="true" | |
| > | |
| Memory Graph | |
| </button> | |
| <button | |
| class="graph-tab" | |
| data-graph-kind="constraints" | |
| type="button" | |
| role="tab" | |
| aria-selected="false" | |
| > | |
| Last Constraint Graph | |
| </button> | |
| </div> | |
| <div id="graph-controls" class="graph-controls" aria-label="Memory graph display options"> | |
| <label> | |
| <span>Mode</span> | |
| <select id="graph-mode-select"> | |
| <option value="slice">Weighted slice</option> | |
| <option value="all">All</option> | |
| <option value="most_used">Most used</option> | |
| <option value="neighborhood">Latest phrase</option> | |
| </select> | |
| </label> | |
| <label> | |
| <span>Order</span> | |
| <select id="graph-order-select"> | |
| <option value="all">All orders</option> | |
| </select> | |
| </label> | |
| </div> | |
| <div class="graph-image-frame"> | |
| <img | |
| id="graph-image" | |
| alt="Continuator internal graph snapshot" | |
| /> | |
| </div> | |
| </aside> | |
| <main class="layout"> | |
| <section class="column"> | |
| <article class="card control-workspace"> | |
| <div class="card-head control-head"> | |
| <div> | |
| <h2>Studio Controls</h2> | |
| </div> | |
| <div class="status-cluster"> | |
| <span class="status-pill" id="session-status">No session</span> | |
| <span class="status-pill" id="midi-status">Disconnected</span> | |
| <span class="status-pill" id="phrase-status">Waiting for MIDI</span> | |
| </div> | |
| </div> | |
| <div class="workspace-tabs" role="tablist" aria-label="Studio control views"> | |
| <button | |
| class="workspace-tab is-active" | |
| data-control-tab="perform" | |
| type="button" | |
| role="tab" | |
| aria-selected="true" | |
| aria-controls="control-perform-panel" | |
| > | |
| Perform | |
| </button> | |
| <button | |
| class="workspace-tab" | |
| data-control-tab="memory" | |
| type="button" | |
| role="tab" | |
| aria-selected="false" | |
| aria-controls="control-memory-panel" | |
| > | |
| Memory | |
| </button> | |
| <button | |
| class="workspace-tab" | |
| data-control-tab="session" | |
| type="button" | |
| role="tab" | |
| aria-selected="false" | |
| aria-controls="control-session-panel" | |
| > | |
| Session | |
| </button> | |
| </div> | |
| <div class="performance-strip"> | |
| <div class="readiness-steps" aria-label="Performance readiness"> | |
| <span id="ready-session-step" class="readiness-step">Session</span> | |
| <span id="ready-midi-step" class="readiness-step">MIDI</span> | |
| <span id="ready-phrase-step" class="readiness-step">Phrase</span> | |
| <span id="ready-output-step" class="readiness-step">Output</span> | |
| </div> | |
| <button id="global-panic-button" class="ghost danger-button global-panic-button" type="button"> | |
| Stop / Panic | |
| </button> | |
| </div> | |
| <section | |
| id="control-perform-panel" | |
| class="workspace-panel" | |
| data-control-panel="perform" | |
| role="tabpanel" | |
| > | |
| <div class="panel-stack"> | |
| <section class="panel-section accent-section"> | |
| <div class="section-head"> | |
| <div> | |
| <h3>Live Input</h3> | |
| </div> | |
| </div> | |
| <div class="button-row"> | |
| <button id="connect-midi-button">Connect MIDI</button> | |
| <button id="refresh-midi-button" class="ghost">Refresh Ports</button> | |
| <label class="toggle"> | |
| <input id="auto-send-toggle" type="checkbox" checked /> | |
| <span>Auto-send phrase</span> | |
| </label> | |
| <label class="toggle"> | |
| <input id="input-monitor-toggle" type="checkbox" /> | |
| <span>Monitor input</span> | |
| </label> | |
| </div> | |
| <div class="field"> | |
| <label for="midi-input-select">MIDI Input</label> | |
| <select id="midi-input-select" disabled> | |
| <option>Connect MIDI first</option> | |
| </select> | |
| </div> | |
| <div | |
| id="virtual-keyboard-panel" | |
| class="virtual-keyboard-panel" | |
| aria-label="Virtual MIDI keyboard" | |
| hidden | |
| > | |
| <div class="virtual-keyboard-head"> | |
| <div> | |
| <strong>Virtual MIDI Keyboard</strong> | |
| <span id="virtual-octave-label">C4-B5</span> | |
| </div> | |
| <div class="virtual-octave-controls" aria-label="Octave controls"> | |
| <button id="virtual-octave-down-button" class="ghost compact-button" type="button"> | |
| Oct - | |
| </button> | |
| <button id="virtual-octave-up-button" class="ghost compact-button" type="button"> | |
| Oct + | |
| </button> | |
| </div> | |
| </div> | |
| <div class="virtual-keyboard-controls"> | |
| <label class="velocity-control" for="virtual-velocity-input"> | |
| <span>Velocity</span> | |
| <input | |
| id="virtual-velocity-input" | |
| type="range" | |
| min="1" | |
| max="127" | |
| value="100" | |
| /> | |
| <output id="virtual-velocity-value" for="virtual-velocity-input">100</output> | |
| </label> | |
| <label class="toggle compact-toggle"> | |
| <input id="virtual-sustain-toggle" type="checkbox" /> | |
| <span>Sustain</span> | |
| </label> | |
| <label class="toggle compact-toggle"> | |
| <input id="virtual-latch-toggle" type="checkbox" /> | |
| <span>Latch chord</span> | |
| </label> | |
| </div> | |
| <div class="virtual-keyboard-viewport"> | |
| <div | |
| id="virtual-keyboard" | |
| class="virtual-keyboard" | |
| aria-label="Playable piano keyboard" | |
| ></div> | |
| </div> | |
| <div class="virtual-keyboard-actions"> | |
| <button | |
| id="virtual-clear-latch-button" | |
| class="ghost compact-button" | |
| type="button" | |
| aria-label="Clear latched chord" | |
| > | |
| Clear chord | |
| </button> | |
| <button | |
| id="virtual-panic-button" | |
| class="ghost danger-button compact-button" | |
| type="button" | |
| aria-label="All notes off" | |
| > | |
| Panic | |
| </button> | |
| </div> | |
| </div> | |
| <div class="field"> | |
| <label for="phrase-timeout-input">Phrase gap (seconds)</label> | |
| <input | |
| id="phrase-timeout-input" | |
| type="number" | |
| min="0.2" | |
| max="5" | |
| step="0.1" | |
| value="1" | |
| inputmode="decimal" | |
| /> | |
| <p class="field-hint"> | |
| Capture completes after this delay once all notes are released. | |
| </p> | |
| <div id="phrase-gap-meter" class="phrase-gap-meter" hidden> | |
| <div class="phrase-gap-meter-head"> | |
| <span id="phrase-gap-meter-label">Listening for more notes</span> | |
| <strong id="phrase-gap-meter-copy">1.0s</strong> | |
| </div> | |
| <div class="phrase-gap-track" aria-hidden="true"> | |
| <div id="phrase-gap-meter-bar" class="phrase-gap-bar"></div> | |
| </div> | |
| </div> | |
| </div> | |
| <label class="toggle"> | |
| <input id="learn-input-toggle" type="checkbox" checked /> | |
| <span>Learn the played phrase before generating</span> | |
| </label> | |
| </section> | |
| <section class="panel-section"> | |
| <div class="section-head"> | |
| <div> | |
| <h3>Phrase Flow</h3> | |
| <p class="section-copy"> | |
| Send, replay, or keep chaining continuations from the | |
| current phrase. | |
| </p> | |
| </div> | |
| </div> | |
| <div class="button-row"> | |
| <button id="send-phrase-button">Send Phrase</button> | |
| <button id="generate-memory-button" class="ghost"> | |
| Generate From Memory | |
| </button> | |
| <button id="replay-generated-button" class="ghost">Replay Output</button> | |
| <button id="clear-phrase-button" class="ghost">Clear</button> | |
| </div> | |
| <div class="generation-grid"> | |
| <div class="field generation-field"> | |
| <label for="continuation-length-input">Continuation length (notes)</label> | |
| <input | |
| id="continuation-length-input" | |
| type="number" | |
| min="1" | |
| step="1" | |
| inputmode="numeric" | |
| placeholder="Same as input" | |
| /> | |
| <p class="field-hint"> | |
| Leave empty to match the captured phrase note count. | |
| </p> | |
| </div> | |
| <div class="field generation-field"> | |
| <label>Infinite mode</label> | |
| <div class="infinite-transport"> | |
| <div class="transport-copy"> | |
| <strong id="infinite-state-label">Idle</strong> | |
| <span id="infinite-seed-label">Seed: play or preview a phrase</span> | |
| </div> | |
| <div class="transport-actions"> | |
| <button id="start-infinite-button" class="ghost transport-button" type="button"> | |
| Play | |
| </button> | |
| <button | |
| id="stop-infinite-button" | |
| class="ghost danger-button transport-button" | |
| type="button" | |
| disabled | |
| > | |
| Stop | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <section class="panel-section accent-section"> | |
| <div class="section-head"> | |
| <div> | |
| <h3>Output</h3> | |
| </div> | |
| </div> | |
| <div class="route-picker"> | |
| <label class="route-picker-label" for="midi-output-select">Renderer</label> | |
| <select id="midi-output-select" disabled> | |
| <option>Browser Triangle</option> | |
| </select> | |
| </div> | |
| <div class="route-picker"> | |
| <label class="route-picker-label" for="audio-output-select">Sound Output</label> | |
| <select id="audio-output-select" disabled> | |
| <option>System default</option> | |
| </select> | |
| </div> | |
| <div class="playback-status-row"> | |
| <span class="playback-status-chip"> | |
| <span>Health</span> | |
| <strong id="renderer-health">Browser renderer ready</strong> | |
| </span> | |
| </div> | |
| <div class="sound-actions"> | |
| <button id="test-note-button" class="ghost" type="button"> | |
| Test Note | |
| </button> | |
| <button id="panic-button" class="ghost danger-button" type="button"> | |
| Panic | |
| </button> | |
| </div> | |
| <p class="field-hint compact-hint"> | |
| External MIDI appears after MIDI is connected. | |
| </p> | |
| </section> | |
| <section id="faust-renderer-panel" class="panel-section accent-section" hidden> | |
| <div class="section-head"> | |
| <div> | |
| <h3>Instrument Settings</h3> | |
| </div> | |
| <div id="faust-renderer-status" class="status-pill">Idle</div> | |
| </div> | |
| <div id="faust-clavier-panel"> | |
| <div class="faust-control-grid"> | |
| <div class="field faust-field"> | |
| <div class="faust-label-row"> | |
| <label for="faust-brightness-input">Brightness</label> | |
| <output id="faust-brightness-value" for="faust-brightness-input">0.58</output> | |
| </div> | |
| <input | |
| id="faust-brightness-input" | |
| type="range" | |
| min="0" | |
| max="1" | |
| step="0.01" | |
| value="0.58" | |
| /> | |
| </div> | |
| <div class="field faust-field"> | |
| <div class="faust-label-row"> | |
| <label for="faust-hardness-input">Hardness</label> | |
| <output id="faust-hardness-value" for="faust-hardness-input">0.34</output> | |
| </div> | |
| <input | |
| id="faust-hardness-input" | |
| type="range" | |
| min="0" | |
| max="1" | |
| step="0.01" | |
| value="0.34" | |
| /> | |
| </div> | |
| <div class="field faust-field"> | |
| <div class="faust-label-row"> | |
| <label for="faust-damping-input">Damping</label> | |
| <output id="faust-damping-value" for="faust-damping-input">0.42</output> | |
| </div> | |
| <input | |
| id="faust-damping-input" | |
| type="range" | |
| min="0" | |
| max="1" | |
| step="0.01" | |
| value="0.42" | |
| /> | |
| </div> | |
| <div class="field faust-field"> | |
| <div class="faust-label-row"> | |
| <label for="faust-release-input">Release</label> | |
| <output id="faust-release-value" for="faust-release-input">0.95 s</output> | |
| </div> | |
| <input | |
| id="faust-release-input" | |
| type="range" | |
| min="0.08" | |
| max="4" | |
| step="0.01" | |
| value="0.95" | |
| /> | |
| </div> | |
| <div class="field faust-field"> | |
| <div class="faust-label-row"> | |
| <label for="faust-body-input">Body</label> | |
| <output id="faust-body-value" for="faust-body-input">0.33</output> | |
| </div> | |
| <input | |
| id="faust-body-input" | |
| type="range" | |
| min="0" | |
| max="1" | |
| step="0.01" | |
| value="0.33" | |
| /> | |
| </div> | |
| <div class="field faust-field"> | |
| <div class="faust-label-row"> | |
| <label for="faust-stereo-input">Stereo</label> | |
| <output id="faust-stereo-value" for="faust-stereo-input">0.45</output> | |
| </div> | |
| <input | |
| id="faust-stereo-input" | |
| type="range" | |
| min="0" | |
| max="1" | |
| step="0.01" | |
| value="0.45" | |
| /> | |
| </div> | |
| </div> | |
| </div> | |
| <div id="faust-custom-panel" hidden> | |
| <div class="section-head faust-section-head"> | |
| <div> | |
| <h4>Custom Faust</h4> | |
| <p class="section-copy"> | |
| Paste a polyphonic Faust DSP with hidden | |
| <code>freq</code>, <code>gain</code>, and | |
| <code>gate</code> controls, then compile it locally. | |
| </p> | |
| </div> | |
| <div class="button-row faust-editor-actions"> | |
| <button id="faust-custom-compile-button" type="button"> | |
| Compile & Use | |
| </button> | |
| <button | |
| id="faust-custom-reset-button" | |
| class="ghost" | |
| type="button" | |
| > | |
| Restore Starter | |
| </button> | |
| </div> | |
| </div> | |
| <div class="field faust-editor-field"> | |
| <label for="faust-custom-code-input">DSP Source</label> | |
| <textarea | |
| id="faust-custom-code-input" | |
| class="faust-code-input" | |
| spellcheck="false" | |
| autocapitalize="off" | |
| autocomplete="off" | |
| ></textarea> | |
| <p id="faust-custom-dirty-state" class="field-hint"> | |
| Edits stay local to this browser. Compile to turn the | |
| latest source into the active renderer. | |
| </p> | |
| </div> | |
| <div class="section-head faust-section-head"> | |
| <div> | |
| <h4>Custom Parameters</h4> | |
| <p class="section-copy"> | |
| Visible Faust controls appear here automatically after a | |
| successful compile. | |
| </p> | |
| </div> | |
| </div> | |
| <div id="faust-custom-controls" class="faust-control-grid"></div> | |
| <p id="faust-custom-controls-hint" class="field-hint"> | |
| Compile a polyphonic instrument to expose its parameters. | |
| </p> | |
| </div> | |
| <p id="faust-renderer-hint" class="field-hint"> | |
| Faust instruments are compiled locally in your browser the | |
| first time you use them. | |
| </p> | |
| </section> | |
| </div> | |
| </section> | |
| <section | |
| id="control-memory-panel" | |
| class="workspace-panel" | |
| data-control-panel="memory" | |
| role="tabpanel" | |
| hidden | |
| > | |
| <div class="panel-stack"> | |
| <section class="panel-section accent-section"> | |
| <div class="section-head"> | |
| <div> | |
| <h3>Corpus Import</h3> | |
| <p class="section-copy"> | |
| Load existing MIDI material into the current session so it | |
| becomes part of the live Continuator memory. | |
| </p> | |
| </div> | |
| </div> | |
| <div class="button-row"> | |
| <button | |
| id="import-midi-files-button" | |
| class="ghost" | |
| type="button" | |
| > | |
| Import MIDI Files | |
| </button> | |
| <button | |
| id="import-midi-folder-button" | |
| class="ghost" | |
| type="button" | |
| > | |
| Import MIDI Folder | |
| </button> | |
| </div> | |
| <p class="field-hint"> | |
| Imported phrases are learned into the active session memory | |
| and logged like other learned inputs. | |
| </p> | |
| <input | |
| id="midi-import-input" | |
| type="file" | |
| accept=".mid,.midi,audio/midi" | |
| multiple | |
| hidden | |
| /> | |
| <input | |
| id="midi-folder-import-input" | |
| type="file" | |
| accept=".mid,.midi,audio/midi" | |
| multiple | |
| webkitdirectory | |
| directory | |
| hidden | |
| /> | |
| </section> | |
| <div class="panel-grid"> | |
| <section class="panel-section"> | |
| <div class="section-head"> | |
| <div> | |
| <h3>Style Memory</h3> | |
| <p class="section-copy"> | |
| Listen to what the Continuator currently knows as its | |
| musical vocabulary. | |
| </p> | |
| </div> | |
| <button | |
| id="show-graph-button" | |
| class="ghost head-action" | |
| type="button" | |
| aria-haspopup="dialog" | |
| aria-expanded="false" | |
| aria-controls="graph-panel" | |
| disabled | |
| > | |
| Show Memory Graph | |
| </button> | |
| </div> | |
| <div id="memory-summary" class="settings-summary"> | |
| <span class="settings-chip">No active memory yet</span> | |
| </div> | |
| <p id="memory-hint" class="small-copy memory-hint"> | |
| Create a session and play a phrase to inspect the active Continuator memory. | |
| </p> | |
| <div id="memory-ribbon" class="memory-ribbon"></div> | |
| <div id="memory-list" class="history-list memory-list"> | |
| <p class="muted">No active memory to show yet.</p> | |
| </div> | |
| </section> | |
| <section class="panel-section"> | |
| <div class="section-head"> | |
| <div> | |
| <h3>Phrase History</h3> | |
| <p class="section-copy"> | |
| Follow the phrases that were played, learned, and generated in this session. | |
| </p> | |
| </div> | |
| </div> | |
| <div id="history-list" class="history-list"> | |
| <p class="muted">Create a session to start building history.</p> | |
| </div> | |
| </section> | |
| </div> | |
| </div> | |
| </section> | |
| <section | |
| id="control-session-panel" | |
| class="workspace-panel" | |
| data-control-panel="session" | |
| role="tabpanel" | |
| hidden | |
| > | |
| <div class="panel-stack"> | |
| <section class="panel-section"> | |
| <div class="section-head"> | |
| <div> | |
| <h3>Current Session</h3> | |
| </div> | |
| </div> | |
| <p id="session-save-note" class="session-save-note"> | |
| Guest session: sign in before creating a new session if you want it saved. | |
| </p> | |
| <div class="button-row"> | |
| <button id="create-session-button">Create Session</button> | |
| <button id="reset-session-button" class="ghost">Reset Memory</button> | |
| <button id="save-session-midi-button" class="ghost"> | |
| Save Session MIDI | |
| </button> | |
| <label class="toggle compact-toggle"> | |
| <input id="remember-midi-export-file-toggle" type="checkbox" /> | |
| <span>Don't ask again</span> | |
| </label> | |
| </div> | |
| <p | |
| id="session-midi-save-result" | |
| class="session-midi-save-result" | |
| hidden | |
| ></p> | |
| <div class="settings-compact"> | |
| <div class="settings-label-row"> | |
| <span class="small-copy">Current settings</span> | |
| </div> | |
| <div id="settings-summary" class="settings-summary"></div> | |
| </div> | |
| <details class="settings-panel"> | |
| <summary>Engine and Memory Settings</summary> | |
| <div class="settings-grid"> | |
| <div class="field"> | |
| <label for="engine-kind-select">Engine</label> | |
| <select id="engine-kind-select"> | |
| <option value="classic">Classic Continuator</option> | |
| <option value="context_bp">ContextBPContinuator</option> | |
| <option value="vo_regular_bp">VO Regular BP</option> | |
| </select> | |
| </div> | |
| <label class="toggle settings-toggle"> | |
| <input id="transpose-toggle" type="checkbox" /> | |
| <span>Transpose incoming phrases before learning</span> | |
| </label> | |
| <div class="field"> | |
| <label for="markov-order-input">Markov order K</label> | |
| <input | |
| id="markov-order-input" | |
| type="number" | |
| min="1" | |
| max="16" | |
| step="1" | |
| value="4" | |
| /> | |
| <p class="field-hint"> | |
| Higher K follows longer contexts, but can hit dead ends | |
| sooner. | |
| </p> | |
| </div> | |
| <div class="field"> | |
| <label for="decay-mode-select">Decay mode</label> | |
| <select id="decay-mode-select"> | |
| <option value="full">Full</option> | |
| <option value="late">Late</option> | |
| <option value="middle">Middle</option> | |
| <option value="early">Early</option> | |
| </select> | |
| </div> | |
| <label class="toggle settings-toggle"> | |
| <input id="forget-toggle" type="checkbox" /> | |
| <span>Forget old phrases and keep a rolling memory</span> | |
| </label> | |
| <div class="field"> | |
| <label for="keep-last-input">Keep only N last inputs</label> | |
| <input | |
| id="keep-last-input" | |
| type="number" | |
| min="1" | |
| max="500" | |
| step="1" | |
| value="20" | |
| /> | |
| <p class="field-hint"> | |
| This cap is used when forgetting is enabled. | |
| </p> | |
| </div> | |
| </div> | |
| <div class="button-row"> | |
| <button id="apply-settings-button" class="ghost"> | |
| Apply to Current Session | |
| </button> | |
| </div> | |
| <p class="small-copy settings-copy"> | |
| New sessions start with these settings. Apply Settings | |
| updates the running session without clearing memory. | |
| </p> | |
| </details> | |
| <dl class="info-grid"> | |
| <div> | |
| <dt>Session ID</dt> | |
| <dd id="session-id">Not created yet</dd> | |
| </div> | |
| <div> | |
| <dt>Server</dt> | |
| <dd id="server-status">Checking…</dd> | |
| </div> | |
| <div> | |
| <dt>Continuator</dt> | |
| <dd id="continuator-version">Checking…</dd> | |
| </div> | |
| </dl> | |
| </section> | |
| <section class="panel-section"> | |
| <div class="section-head"> | |
| <div> | |
| <h3>Saved Sessions</h3> | |
| <p id="saved-sessions-copy" class="section-copy"> | |
| Sign in to reopen sessions saved under your account. | |
| </p> | |
| </div> | |
| <button | |
| id="refresh-sessions-button" | |
| class="ghost head-action" | |
| type="button" | |
| > | |
| Refresh | |
| </button> | |
| </div> | |
| <div id="my-sessions-list" class="history-list"> | |
| <p class="muted">Sign in to see your saved sessions.</p> | |
| </div> | |
| <div id="saved-session-preview" class="session-preview-panel" hidden> | |
| <div class="session-preview-head"> | |
| <div> | |
| <span class="small-copy">Selected saved session</span> | |
| <strong id="saved-session-preview-name">No session selected</strong> | |
| </div> | |
| <button | |
| id="saved-session-preview-open-button" | |
| class="ghost session-open-button" | |
| type="button" | |
| > | |
| Open | |
| </button> | |
| </div> | |
| <div class="session-name-editor"> | |
| <label for="saved-session-name-input">Name</label> | |
| <div class="session-name-row"> | |
| <input | |
| id="saved-session-name-input" | |
| type="text" | |
| maxlength="80" | |
| autocomplete="off" | |
| /> | |
| <button id="rename-session-button" class="ghost" type="button"> | |
| Rename | |
| </button> | |
| </div> | |
| </div> | |
| <dl class="session-preview-grid"> | |
| <div> | |
| <dt>Memory</dt> | |
| <dd id="saved-session-preview-memory">-</dd> | |
| </div> | |
| <div> | |
| <dt>History</dt> | |
| <dd id="saved-session-preview-history">-</dd> | |
| </div> | |
| <div> | |
| <dt>Settings</dt> | |
| <dd id="saved-session-preview-settings">-</dd> | |
| </div> | |
| <div> | |
| <dt>Last Seen</dt> | |
| <dd id="saved-session-preview-seen">-</dd> | |
| </div> | |
| </dl> | |
| </div> | |
| </section> | |
| </div> | |
| </section> | |
| </article> | |
| </section> | |
| <section class="column wide-column"> | |
| <article class="card timeline-card"> | |
| <div class="card-head"> | |
| <h2>Phrase Timeline</h2> | |
| </div> | |
| <div id="phrase-timeline" class="phrase-timeline"> | |
| <div id="timeline-captured" class="timeline-segment"> | |
| <span>Captured</span> | |
| <strong>empty</strong> | |
| </div> | |
| <div id="timeline-generated" class="timeline-segment"> | |
| <span>Current Reply</span> | |
| <strong>empty</strong> | |
| </div> | |
| <div id="timeline-queued" class="timeline-segment"> | |
| <span>Next Queued</span> | |
| <strong>none</strong> | |
| </div> | |
| </div> | |
| </article> | |
| <article class="card roll-card"> | |
| <div class="card-head"> | |
| <h2>Captured Phrase</h2> | |
| <span class="small-copy">Detected from browser MIDI events</span> | |
| </div> | |
| <canvas | |
| id="input-roll" | |
| class="piano-roll" | |
| role="button" | |
| tabindex="0" | |
| aria-label="Captured phrase piano roll. Click a note to audition it, or click empty space to play the phrase." | |
| ></canvas> | |
| </article> | |
| <article class="card roll-card output-card"> | |
| <div class="card-head"> | |
| <h2>Generated Continuation</h2> | |
| <span class="small-copy">Returned by Continuator</span> | |
| </div> | |
| <div class="roll-viewport"> | |
| <canvas | |
| id="output-roll" | |
| class="piano-roll" | |
| role="button" | |
| tabindex="0" | |
| aria-label="Generated continuation piano roll. Click a note to audition it, or click empty space to play the continuation." | |
| ></canvas> | |
| <div id="output-playhead" class="roll-playhead" hidden></div> | |
| </div> | |
| </article> | |
| <article class="card"> | |
| <div class="card-head"> | |
| <h2>Live Snapshot</h2> | |
| <span class="small-copy">Always-visible capture and generation feedback</span> | |
| </div> | |
| <dl class="info-grid"> | |
| <div> | |
| <dt>Captured Events</dt> | |
| <dd id="captured-event-count">0</dd> | |
| </div> | |
| <div> | |
| <dt>Captured Notes</dt> | |
| <dd id="captured-note-count">0</dd> | |
| </div> | |
| <div> | |
| <dt>Generated Events</dt> | |
| <dd id="generated-event-count">0</dd> | |
| </div> | |
| <div> | |
| <dt>Generated Notes</dt> | |
| <dd id="generated-note-count">0</dd> | |
| </div> | |
| </dl> | |
| <div class="message-box" id="message-box"> | |
| Play a short phrase on the selected MIDI input to capture it. | |
| </div> | |
| <div class="constraint-status" id="constraint-status" hidden></div> | |
| </article> | |
| </section> | |
| </main> | |
| <script type="module" src="/assets/app.js?v=20260602-session-midi-zip3"></script> | |
| </body> | |
| </html> | |