Spaces:
Running on Zero
Running on Zero
Upload app.py
Browse files
app.py
CHANGED
|
@@ -51,6 +51,10 @@ try:
|
|
| 51 |
except Exception:
|
| 52 |
PIANO_LO, PIANO_HI, PIANO_FPS = 56, 86, 8
|
| 53 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
_get_piano() # warm the piano Modular Mind at app startup (so the first play is instant)
|
| 55 |
|
| 56 |
|
|
@@ -183,13 +187,15 @@ FORCE_DARK_JS = """
|
|
| 183 |
# ---- self-playing piano (a Modular Mind trained on the song) -----------------
|
| 184 |
PIANO_CSS = """
|
| 185 |
#mm-piano-wrap{max-width:920px;margin:6px auto 2px;font-family:system-ui,sans-serif}
|
| 186 |
-
#mm-piano{
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
.pk
|
| 191 |
-
|
| 192 |
-
.pk.
|
|
|
|
|
|
|
| 193 |
#mm-piano-ctrl{display:flex;gap:14px;align-items:center;justify-content:center;margin:8px auto 4px}
|
| 194 |
#mm-piano-btn{cursor:pointer;background:#2a9d6a;color:#fff;border:none;border-radius:6px;
|
| 195 |
padding:9px 18px;font-weight:700;font-size:14px}
|
|
@@ -225,6 +231,53 @@ PIANO_JS = r"""
|
|
| 225 |
var specBox=document.getElementById('mm-piano-specs'), latBox=document.getElementById('mm-piano-latent');
|
| 226 |
var NN=['C','C#','D','D#','E','F','F#','G','G#','A','A#','B'];
|
| 227 |
function name(m){return NN[m%12]+(Math.floor(m/12)-1);}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 228 |
function buildSpecs(telem){
|
| 229 |
if(built || !specBox || !telem) return; built=true;
|
| 230 |
telem.spec.forEach(function(s){
|
|
@@ -311,16 +364,16 @@ PIANO_JS = r"""
|
|
| 311 |
}catch(e){}
|
| 312 |
}
|
| 313 |
function releaseAll(){
|
| 314 |
-
for(var mk in voices){ releaseNode(voices[mk]);
|
| 315 |
voices={};
|
| 316 |
}
|
| 317 |
function playFrame(midis){ // polyphony: strike new notes, hold sustained ones, release dropped ones
|
| 318 |
var nw={}; (midis||[]).forEach(function(m){ if(m>0) nw[m]=1; });
|
| 319 |
for(var mk in voices){ if(!nw[mk]){ releaseNode(voices[mk]);
|
| 320 |
-
|
| 321 |
var on=Object.keys(nw), vol=on.length>2?0.5:0.65;
|
| 322 |
on.forEach(function(ms){ var m=+ms; if(!voices[m]){ var v=voice(m,vol); if(v) voices[m]=v;
|
| 323 |
-
|
| 324 |
if(noteEl){ noteEl.textContent= on.length ? ('βͺ '+on.map(function(ms){return name(+ms);}).join(' ')) : 'βͺ (rest)'; }
|
| 325 |
}
|
| 326 |
function tick(){ if(!playing) return; if(queue.length<10 && !fetching) fetchPhrase();
|
|
@@ -329,7 +382,7 @@ PIANO_JS = r"""
|
|
| 329 |
if(!audio) audio=new (window.AudioContext||window.webkitAudioContext)();
|
| 330 |
if(audio.state==='suspended'){ try{audio.resume();}catch(e){} }
|
| 331 |
loadSamples(); // real piano loads in background; oscillator plays until then
|
| 332 |
-
playing=true; btn.textContent='βΈ Pause';
|
| 333 |
if(queue.length===0) fetchPhrase();
|
| 334 |
if(!timer) timer=setInterval(tick, PLAY_MS);
|
| 335 |
}
|
|
@@ -342,12 +395,15 @@ PIANO_JS = r"""
|
|
| 342 |
"""
|
| 343 |
PIANO_HTML = """
|
| 344 |
<div id="mm-piano-wrap">
|
| 345 |
-
<div id="mm-piano">
|
|
|
|
|
|
|
|
|
|
| 346 |
<div id="mm-piano-ctrl">
|
| 347 |
<button id="mm-piano-btn">βΆ Let the Modular Mind play</button>
|
| 348 |
<span id="mm-piano-note">βͺ</span>
|
| 349 |
</div>
|
| 350 |
-
<div id="mm-piano-lbl">
|
| 351 |
<div id="mm-piano-specs"></div>
|
| 352 |
<div id="mm-piano-latent" title="RecursiveLink shared latent"></div>
|
| 353 |
</div>
|
|
@@ -369,7 +425,7 @@ HEAD = (
|
|
| 369 |
)
|
| 370 |
|
| 371 |
INTRO = """
|
| 372 |
-
# π
|
| 373 |
A mini **Dark-Souls-style** duel where the boss is controlled by a **Modular Mind** β six tiny
|
| 374 |
specialist networks that communicate through a **shared latent** (RecursiveLink) and a coordinator
|
| 375 |
that picks each move. The brain was **trained by self-play reinforcement learning**, not scripted.
|
|
@@ -378,9 +434,15 @@ steer the fight through the latent. **Click *Enter the Fog* and click the game o
|
|
| 378 |
"""
|
| 379 |
|
| 380 |
# ---- placeholder repo link (replace REPO_URL with your real GitHub URL) ------
|
| 381 |
-
REPO_URL = "" # TODO: replace with the real repo
|
| 382 |
REPO_MD = f"""
|
|
|
|
|
|
|
|
|
|
|
|
|
| 383 |
|
|
|
|
|
|
|
| 384 |
"""
|
| 385 |
|
| 386 |
TECH_MD = r"""
|
|
@@ -482,12 +544,14 @@ Under the boss fight, the *same architecture* (tiny specialists β `RecursiveLi
|
|
| 482 |
applied to **playing piano in chords**. It was trained by **multi-note next-frame prediction** on a
|
| 483 |
*polyphonic* transcription of a song: six specialists (Bass / Tenor / Soprano registers + Sustain /
|
| 484 |
Onset / Phrase modulators) emit latents, the bridge merges them, and the coordinator picks the **set
|
| 485 |
-
of notes** to play next. It plays itself with **real recorded acoustic-piano samples**
|
| 486 |
-
|
|
|
|
|
|
|
| 487 |
<sub>Rough by design β one song, a tiny model, crude polyphonic transcription β the *method carrying over* is the point.</sub>
|
| 488 |
"""
|
| 489 |
|
| 490 |
-
with gr.Blocks(
|
| 491 |
gr.Markdown(INTRO)
|
| 492 |
gr.HTML(INDEX_HTML)
|
| 493 |
|
|
@@ -526,4 +590,7 @@ if __name__ == "__main__":
|
|
| 526 |
server_name="0.0.0.0",
|
| 527 |
server_port=int(os.environ.get("PORT", "7860")),
|
| 528 |
allowed_paths=[AUDIO_DIR, PIANO_SAMPLES_DIR],
|
|
|
|
|
|
|
|
|
|
| 529 |
)
|
|
|
|
| 51 |
except Exception:
|
| 52 |
PIANO_LO, PIANO_HI, PIANO_FPS = 56, 86, 8
|
| 53 |
|
| 54 |
+
# the performance is restyled live into A minor with the bass lifted away (see
|
| 55 |
+
# piano/poly_mind.py stylize_midi), so the on-screen keyboard starts at middle C
|
| 56 |
+
PIANO_LO = max(PIANO_LO, 60)
|
| 57 |
+
|
| 58 |
_get_piano() # warm the piano Modular Mind at app startup (so the first play is instant)
|
| 59 |
|
| 60 |
|
|
|
|
| 187 |
# ---- self-playing piano (a Modular Mind trained on the song) -----------------
|
| 188 |
PIANO_CSS = """
|
| 189 |
#mm-piano-wrap{max-width:920px;margin:6px auto 2px;font-family:system-ui,sans-serif}
|
| 190 |
+
#mm-piano-stage{position:relative;background:radial-gradient(ellipse at 50% 110%,#1b1430 0%,#0c0c14 62%,#08080e 100%);
|
| 191 |
+
border:1px solid #2a2a35;border-radius:10px;padding:0 8px 12px;overflow-x:auto;overflow-y:hidden}
|
| 192 |
+
#mm-piano-roll{display:block;width:100%;height:150px}
|
| 193 |
+
#mm-piano{display:flex;align-items:flex-end;justify-content:center;gap:2px;height:112px}
|
| 194 |
+
.pk{box-sizing:border-box;border:1px solid #05050a;border-radius:0 0 4px 4px;flex:0 0 auto;
|
| 195 |
+
transition:background .07s ease,box-shadow .07s ease}
|
| 196 |
+
.pk.white{width:20px;height:100px;background:linear-gradient(180deg,#f4f4f8 0%,#d6d6e0 88%,#b9b9c6 100%)}
|
| 197 |
+
.pk.black{width:14px;height:62px;background:linear-gradient(180deg,#3a3a46 0%,#1b1b23 100%)}
|
| 198 |
+
.pk.on{transform:translateY(1px)}
|
| 199 |
#mm-piano-ctrl{display:flex;gap:14px;align-items:center;justify-content:center;margin:8px auto 4px}
|
| 200 |
#mm-piano-btn{cursor:pointer;background:#2a9d6a;color:#fff;border:none;border-radius:6px;
|
| 201 |
padding:9px 18px;font-weight:700;font-size:14px}
|
|
|
|
| 231 |
var specBox=document.getElementById('mm-piano-specs'), latBox=document.getElementById('mm-piano-latent');
|
| 232 |
var NN=['C','C#','D','D#','E','F','F#','G','G#','A','A#','B'];
|
| 233 |
function name(m){return NN[m%12]+(Math.floor(m/12)-1);}
|
| 234 |
+
// ---- light show: glowing note trails rise off the keys while they sound ----
|
| 235 |
+
var roll=document.getElementById('mm-piano-roll');
|
| 236 |
+
var rctx=roll?roll.getContext('2d'):null, trails=[], sparks=[], keyTrail={}, rafOn=false;
|
| 237 |
+
function hue(m){return ((m%12)*30+200)%360;} // pitch class -> color wheel
|
| 238 |
+
function ensureRaf(){ if(rctx && !rafOn){ rafOn=true; requestAnimationFrame(draw); } }
|
| 239 |
+
function draw(){
|
| 240 |
+
var w=roll.clientWidth, hgt=roll.clientHeight, now=performance.now(), v=0.05;
|
| 241 |
+
if(roll.width!==w) roll.width=w; if(roll.height!==hgt) roll.height=hgt;
|
| 242 |
+
rctx.clearRect(0,0,w,hgt);
|
| 243 |
+
for(var i=trails.length-1;i>=0;i--){ var tr=trails[i];
|
| 244 |
+
var top=hgt-(now-tr.t0)*v, bot=hgt-(tr.t1?(now-tr.t1)*v:0);
|
| 245 |
+
if(bot<-30){ trails.splice(i,1); continue; }
|
| 246 |
+
top=Math.max(top,-30);
|
| 247 |
+
var g=rctx.createLinearGradient(0,top,0,bot);
|
| 248 |
+
g.addColorStop(0,'hsla('+tr.h+',85%,62%,0)');
|
| 249 |
+
g.addColorStop(1,'hsla('+tr.h+',85%,62%,0.9)');
|
| 250 |
+
rctx.shadowColor='hsl('+tr.h+',85%,60%)'; rctx.shadowBlur=10;
|
| 251 |
+
rctx.fillStyle=g; rctx.fillRect(tr.x,top,tr.w,Math.max(2,bot-top));
|
| 252 |
+
}
|
| 253 |
+
rctx.shadowBlur=0;
|
| 254 |
+
for(var j=sparks.length-1;j>=0;j--){ var s=sparks[j], a=1-(now-s.t0)/650;
|
| 255 |
+
if(a<=0){ sparks.splice(j,1); continue; }
|
| 256 |
+
s.x+=s.vx; s.y+=s.vy;
|
| 257 |
+
rctx.fillStyle='hsla('+s.h+',95%,72%,'+a.toFixed(2)+')';
|
| 258 |
+
rctx.fillRect(s.x,s.y,2.2,2.2);
|
| 259 |
+
}
|
| 260 |
+
if(!playing && !trails.length && !sparks.length){ rafOn=false; rctx.clearRect(0,0,w,hgt); return; }
|
| 261 |
+
requestAnimationFrame(draw);
|
| 262 |
+
}
|
| 263 |
+
function strikeFx(m,el){
|
| 264 |
+
if(!rctx||!el) return;
|
| 265 |
+
var x=el.offsetLeft-roll.offsetLeft, wd=el.offsetWidth, hh=hue(m), now=performance.now();
|
| 266 |
+
var tr={x:x,w:wd,h:hh,t0:now,t1:null};
|
| 267 |
+
trails.push(tr); keyTrail[m]=tr;
|
| 268 |
+
for(var i=0;i<6;i++) sparks.push({x:x+wd/2,y:roll.clientHeight-3,
|
| 269 |
+
vx:(Math.random()-0.5)*1.6, vy:-(0.6+Math.random()*1.4), h:hh, t0:now});
|
| 270 |
+
ensureRaf();
|
| 271 |
+
}
|
| 272 |
+
function endTrail(m){ if(keyTrail[m]){ keyTrail[m].t1=performance.now(); delete keyTrail[m]; } }
|
| 273 |
+
function lightKey(m,on){
|
| 274 |
+
var el=document.getElementById('pk-'+m); if(!el) return el;
|
| 275 |
+
if(on){ el.classList.add('on'); var hh=hue(m);
|
| 276 |
+
el.style.background='hsl('+hh+',82%,'+(BLACK[m%12]?'46%':'68%')+')';
|
| 277 |
+
el.style.boxShadow='0 0 18px hsl('+hh+',85%,60%)';
|
| 278 |
+
} else { el.classList.remove('on'); el.style.background=''; el.style.boxShadow=''; }
|
| 279 |
+
return el;
|
| 280 |
+
}
|
| 281 |
function buildSpecs(telem){
|
| 282 |
if(built || !specBox || !telem) return; built=true;
|
| 283 |
telem.spec.forEach(function(s){
|
|
|
|
| 364 |
}catch(e){}
|
| 365 |
}
|
| 366 |
function releaseAll(){
|
| 367 |
+
for(var mk in voices){ releaseNode(voices[mk]); lightKey(+mk,false); endTrail(+mk); }
|
| 368 |
voices={};
|
| 369 |
}
|
| 370 |
function playFrame(midis){ // polyphony: strike new notes, hold sustained ones, release dropped ones
|
| 371 |
var nw={}; (midis||[]).forEach(function(m){ if(m>0) nw[m]=1; });
|
| 372 |
for(var mk in voices){ if(!nw[mk]){ releaseNode(voices[mk]);
|
| 373 |
+
lightKey(+mk,false); endTrail(+mk); delete voices[mk]; } }
|
| 374 |
var on=Object.keys(nw), vol=on.length>2?0.5:0.65;
|
| 375 |
on.forEach(function(ms){ var m=+ms; if(!voices[m]){ var v=voice(m,vol); if(v) voices[m]=v;
|
| 376 |
+
strikeFx(m, lightKey(m,true)); } });
|
| 377 |
if(noteEl){ noteEl.textContent= on.length ? ('βͺ '+on.map(function(ms){return name(+ms);}).join(' ')) : 'βͺ (rest)'; }
|
| 378 |
}
|
| 379 |
function tick(){ if(!playing) return; if(queue.length<10 && !fetching) fetchPhrase();
|
|
|
|
| 382 |
if(!audio) audio=new (window.AudioContext||window.webkitAudioContext)();
|
| 383 |
if(audio.state==='suspended'){ try{audio.resume();}catch(e){} }
|
| 384 |
loadSamples(); // real piano loads in background; oscillator plays until then
|
| 385 |
+
playing=true; btn.textContent='βΈ Pause'; ensureRaf();
|
| 386 |
if(queue.length===0) fetchPhrase();
|
| 387 |
if(!timer) timer=setInterval(tick, PLAY_MS);
|
| 388 |
}
|
|
|
|
| 395 |
"""
|
| 396 |
PIANO_HTML = """
|
| 397 |
<div id="mm-piano-wrap">
|
| 398 |
+
<div id="mm-piano-stage">
|
| 399 |
+
<canvas id="mm-piano-roll"></canvas>
|
| 400 |
+
<div id="mm-piano"></div>
|
| 401 |
+
</div>
|
| 402 |
<div id="mm-piano-ctrl">
|
| 403 |
<button id="mm-piano-btn">βΆ Let the Modular Mind play</button>
|
| 404 |
<span id="mm-piano-note">βͺ</span>
|
| 405 |
</div>
|
| 406 |
+
<div id="mm-piano-lbl">restyled live into <b>A minor</b> β every note is lifted out of the bass and snapped to the minor scale Β· Bass / Tenor / Soprano own a register; Sustain / Onset / Phrase are modulators that only write to the shared latent</div>
|
| 407 |
<div id="mm-piano-specs"></div>
|
| 408 |
<div id="mm-piano-latent" title="RecursiveLink shared latent"></div>
|
| 409 |
</div>
|
|
|
|
| 425 |
)
|
| 426 |
|
| 427 |
INTRO = """
|
| 428 |
+
# π Quazim0t0's Thousand Token Wood Entry
|
| 429 |
A mini **Dark-Souls-style** duel where the boss is controlled by a **Modular Mind** β six tiny
|
| 430 |
specialist networks that communicate through a **shared latent** (RecursiveLink) and a coordinator
|
| 431 |
that picks each move. The brain was **trained by self-play reinforcement learning**, not scripted.
|
|
|
|
| 434 |
"""
|
| 435 |
|
| 436 |
# ---- placeholder repo link (replace REPO_URL with your real GitHub URL) ------
|
| 437 |
+
REPO_URL = "https://github.com/your-username/ModularMind" # TODO: replace with the real repo
|
| 438 |
REPO_MD = f"""
|
| 439 |
+
### π¦ Get the full Modular Mind project
|
| 440 |
+
This Space runs a **tiny, specialist-scale** version of the architecture. The full,
|
| 441 |
+
production-scale **Modular Mind** β transformer specialists (MLA / MoE / Hyper-Connections),
|
| 442 |
+
the `RecursiveLink` latent bridge, context-doubling and the residual latent highway β lives on GitHub:
|
| 443 |
|
| 444 |
+
### π [**Download / view the Modular Mind repo on GitHub**]({REPO_URL})
|
| 445 |
+
*(placeholder link β swap `{REPO_URL}` for your actual repository)*
|
| 446 |
"""
|
| 447 |
|
| 448 |
TECH_MD = r"""
|
|
|
|
| 544 |
applied to **playing piano in chords**. It was trained by **multi-note next-frame prediction** on a
|
| 545 |
*polyphonic* transcription of a song: six specialists (Bass / Tenor / Soprano registers + Sustain /
|
| 546 |
Onset / Phrase modulators) emit latents, the bridge merges them, and the coordinator picks the **set
|
| 547 |
+
of notes** to play next. It plays itself with **real recorded acoustic-piano samples**, and the
|
| 548 |
+
performance is **restyled live into A minor** β every note is lifted out of the bass register and
|
| 549 |
+
snapped to the minor scale before it reaches the keys. Press **play** and watch each note send a
|
| 550 |
+
glowing trail of light off the keyboard.
|
| 551 |
<sub>Rough by design β one song, a tiny model, crude polyphonic transcription β the *method carrying over* is the point.</sub>
|
| 552 |
"""
|
| 553 |
|
| 554 |
+
with gr.Blocks(title="Quazim0t0's π Thousand Token Wood Entry") as demo:
|
| 555 |
gr.Markdown(INTRO)
|
| 556 |
gr.HTML(INDEX_HTML)
|
| 557 |
|
|
|
|
| 590 |
server_name="0.0.0.0",
|
| 591 |
server_port=int(os.environ.get("PORT", "7860")),
|
| 592 |
allowed_paths=[AUDIO_DIR, PIANO_SAMPLES_DIR],
|
| 593 |
+
# Gradio 6 moved these from the Blocks constructor to launch()
|
| 594 |
+
theme=gr.themes.Base(),
|
| 595 |
+
head=HEAD,
|
| 596 |
)
|