File size: 11,761 Bytes
4fd620e 4bb3f7c 4fd620e 56e2384 4fd620e 4bb3f7c 4fd620e 4cf5cdc 4fd620e 4bb3f7c 4fd620e 4bb3f7c b9cbc2e 4cf5cdc b9cbc2e 4cf5cdc b9cbc2e 4bb3f7c b9cbc2e 3dbd21f 9ae1216 56e2384 4cf5cdc 4bb3f7c 4fd620e 4cf5cdc 4fd620e 4cf5cdc 4fd620e 4cf5cdc 4fd620e b9cbc2e 4fd620e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | <!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>DaisyChain-Web — train by opening a page</title>
<style>
:root {
--page-bg: #efe4c9;
--card-bg: #fbf6e8;
--card-border: rgba(139, 111, 71, 0.30);
--text: #2a1d0a;
--text-soft: #6b4423;
--accent: #4a7c2e;
--accent-deep: #2d5016;
--counter-bg: linear-gradient(135deg, #2d5016 0%, #1f3a0f 100%);
--counter-num: #f5ecd9;
--counter-label: #c9b072;
--btn: linear-gradient(135deg, #4a7c2e 0%, #2d5016 100%);
--btn-hover: linear-gradient(135deg, #5a8c3e 0%, #3d6020 100%);
--warn: #8b2e25;
--link: #4a7c2e;
--track: rgba(139, 111, 71, 0.22);
}
@media (prefers-color-scheme: dark) {
:root {
--page-bg: #14100a; --card-bg: #1f1a12; --card-border: rgba(201, 176, 114, 0.35);
--text: #ede1c3; --text-soft: #c9b072; --accent: #9bc466; --accent-deep: #6b9039;
--counter-bg: linear-gradient(135deg, #1a2e0d 0%, #0c1606 100%);
--counter-label: #c9b072; --warn: #ff9b8e; --link: #9bc466; --track: rgba(201,176,114,0.20);
}
}
* { box-sizing: border-box; }
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
max-width: 720px; margin: 0 auto; padding: 24px 18px 40px; line-height: 1.5;
background: var(--page-bg); color: var(--text); }
h1 { margin: 0 0 2px; font-size: 1.7rem; letter-spacing: .3px; }
.sub { color: var(--text-soft); margin: 0 0 18px; font-size: .92rem; }
.sub code { background: rgba(74,124,46,.12); padding: 1px 5px; border-radius: 4px; }
.card { background: var(--card-bg); border: 1px solid var(--card-border);
border-radius: 10px; padding: 14px 16px; margin: 12px 0;
box-shadow: 0 2px 10px rgba(0,0,0,0.06); }
.lbl { color: var(--text-soft); font-size: 11px; font-weight: 800; letter-spacing: 1.5px;
text-transform: uppercase; margin-bottom: 8px; }
.row { display: flex; justify-content: space-between; gap: 12px; padding: 3px 0; font-size: .95rem; }
.k { color: var(--text-soft); } .v { font-weight: 700; text-align: right; }
.device { font-family: 'Courier New', monospace; font-size: 1.5rem; font-weight: 700;
color: var(--accent-deep); }
@media (prefers-color-scheme: dark) { .device { color: var(--accent); } }
.counter { background: var(--counter-bg); border-radius: 10px; padding: 14px 16px; text-align: center; }
.counter .num { font-family: 'Courier New', monospace; font-size: 2.2rem; font-weight: 700; color: var(--counter-num); }
.counter .cl { color: var(--counter-label); font-size: 11px; text-transform: uppercase; letter-spacing: 1.5px; }
.track { width: 100%; height: 10px; border-radius: 6px; background: var(--track); overflow: hidden; margin-top: 12px; }
#bar { height: 10px; width: 0; background: linear-gradient(90deg, #6b9039, #9bc466); transition: width .25s; }
button { background: var(--btn); color: #f5ecd9; border: 0; border-radius: 8px;
padding: 12px 26px; font-weight: 800; font-size: 1rem; letter-spacing: .5px; cursor: pointer;
box-shadow: 0 3px 10px rgba(74,49,16,0.18); transition: .15s; }
button:hover:not(:disabled) { background: var(--btn-hover); box-shadow: 0 5px 14px rgba(74,49,16,0.28); }
button:disabled { background: #8b7d5e; opacity: .55; cursor: not-allowed; box-shadow: none; }
pre { background: rgba(0,0,0,0.05); border-radius: 8px; padding: 10px; max-height: 150px;
overflow: auto; font-size: .78rem; color: var(--text-soft); white-space: pre-wrap; margin: 0;
font-family: 'Courier New', monospace; }
@media (prefers-color-scheme: dark) { pre { background: rgba(0,0,0,0.25); } }
.note { color: var(--text-soft); font-size: .82rem; }
.slbl { display: flex; justify-content: space-between; font-size: .92rem; margin: 10px 0 4px; font-weight: 600; }
.sval { font-family: 'Courier New', monospace; color: var(--accent); font-weight: 700; }
input[type=range] { width: 100%; accent-color: var(--accent); }
.note b { color: var(--warn); }
.diff { color: var(--accent-deep); font-weight: 600; font-size: .88rem; }
@media (prefers-color-scheme: dark) { .diff { color: var(--accent); } }
</style>
</head>
<body>
<h1>🌼 DaisyChain-Web</h1>
<p class="sub">Open this on your other devices <b>on the same network</b> and they <b>pretrain a language model from scratch</b> together — peer-to-peer, right in the browser, through the emulated GPU logic. This is pretraining, not fine-tuning: every run starts from random weights. Only devices on your network are grouped (like Snapdrop). To invite people across networks, everyone opens <code>?room=YOUR-CODE</code> — the person who created the room approves each device before it can join.</p>
<div class="card" id="lobby" style="display:none;text-align:center">
<div class="lbl">🏡 Get started</div>
<p class="note" style="margin:0 0 12px">Create a room and invite your devices, or join a room someone shared with you.</p>
<button id="createRoom">Create a room</button>
<div style="display:flex;gap:8px;justify-content:center;margin-top:12px;flex-wrap:wrap">
<input type="text" id="joinCode" placeholder="room code" spellcheck="false"
style="padding:10px 12px;border-radius:8px;border:1px solid var(--card-border);background:transparent;color:inherit;font-family:'Courier New',monospace">
<button id="joinRoom" style="padding:10px 18px">Join</button>
</div>
</div>
<div class="card">
<div class="lbl">🌲 This device</div>
<div class="device" id="me">—</div>
<div class="row" style="margin-top:8px"><span class="k">Status</span><span class="v" id="status">starting…</span></div>
<div class="row"><span class="k">Compute</span><span class="v" id="backend">detecting…</span></div>
<div class="row"><span class="k">Dataset</span><span class="v" id="dataset">—</span></div>
<div class="row"><span class="k">Tokenizer</span><span class="v" id="tokenizer">loading…</span></div>
</div>
<div class="card">
<div class="lbl">🍄 Devices in your group</div>
<div class="row"><span class="v" id="peers" style="text-align:left">(none yet)</span></div>
<div id="roomInfo" style="display:none;margin-top:8px">
<div class="row"><span class="k">Room</span><span class="v" id="roomCode"></span></div>
<div style="text-align:center;margin-top:6px"><button id="copyLink" style="padding:6px 14px;font-size:.85rem">Copy invite link</button></div>
<p class="note" style="margin:.5rem 0 0;text-align:center">Open the link on your other devices — you approve each one before it joins.</p>
</div>
<div id="requests"></div>
</div>
<div class="card">
<div class="lbl">🎛 Training settings</div>
<label class="slbl">Model width <span class="sval" id="vcfgC">32</span></label>
<input type="range" id="cfgC" min="16" max="128" step="16" value="32">
<label class="slbl">Sequence length <span class="sval" id="vcfgT">32</span></label>
<input type="range" id="cfgT" min="16" max="128" step="16" value="32">
<p class="note" style="margin:.4rem 0 0"><b>Width or sequence above 64?</b> Bring more devices — big settings on 1–3 devices mean slow steps and a small effective batch. 4+ devices recommended.</p>
<label class="slbl">Batch per device <span class="sval" id="vcfgB">8</span></label>
<input type="range" id="cfgB" min="2" max="32" step="2" value="8">
<label class="slbl">Steps <span class="sval" id="vcfgSteps">300</span></label>
<input type="range" id="cfgSteps" min="100" max="10000" step="100" value="300">
<label class="slbl">Learning rate ×1000 <span class="sval" id="vcfgLr">10</span></label>
<input type="range" id="cfgLr" min="5" max="50" step="5" value="10">
<label class="slbl">Dataset</label>
<p class="note" style="margin:.2rem 0 0"><b>FineWeb-Edu</b> (HuggingFaceFW/fineweb-edu, 10BT sample) — fixed. Each device pulls its own random slice of the parquet shards, served by this Space; if streaming fails, the built-in corpus is used.</p>
<p class="note" style="margin:.6rem 0 0"><b>This is pretraining, not fine-tuning</b> — every run trains a brand-new model from random weights. You are watching a language model learn from scratch.</p>
<p class="note" style="margin:.5rem 0 0">A mini transformer language model — attention, MLP blocks, next-character prediction — every multiply through the verified INT8 units. Training text is streamed from <b>FineWeb-Edu</b> (HuggingFace); offline devices fall back to a built-in corpus. Whoever presses Start sets the settings for the whole group; total batch scales with every device that joins.</p>
</div>
<div class="card" style="text-align:center">
<button id="start" disabled>Start training</button>
<p class="note" style="margin:.6rem 0 0">Works solo — every device that joins adds its batch to the group.</p>
</div>
<div class="card">
<div class="lbl">✦ Training</div>
<div class="row"><span class="k">Step</span><span class="v" id="step">— / —</span></div>
<div class="counter" style="margin:10px 0"><div class="num" id="loss">—</div><div class="cl">cluster-avg loss · lower is better</div></div>
<div class="track"><div id="bar"></div></div>
<div class="row" style="margin-top:10px"><span class="diff" id="diff"></span></div>
</div>
<div class="card">
<div class="lbl">🗣 Test the model</div>
<div style="display:flex;gap:8px;flex-wrap:wrap">
<input type="text" id="genPrompt" value="the " spellcheck="false"
style="flex:1;min-width:160px;padding:10px 12px;border-radius:8px;border:1px solid var(--card-border);background:transparent;color:inherit;font-family:'Courier New',monospace">
<button id="genBtn" disabled style="padding:10px 18px">Generate</button>
</div>
<pre id="genOut" style="margin-top:10px;min-height:44px"></pre>
<p class="note" style="margin:.5rem 0 0">Enabled after training finishes or a checkpoint is loaded. The “inference kit” below downloads a single HTML file with these weights baked in — open it anywhere to run generations offline.</p>
</div>
<div class="card">
<div class="lbl">💾 Model checkpoint</div>
<div style="display:flex;gap:10px;flex-wrap:wrap;justify-content:center">
<button id="save" disabled>Download model (.pt)</button>
<button id="kit" disabled>Download inference kit</button>
<button id="loadBtn">Load checkpoint…</button>
<input type="file" id="load" accept=".pt" style="display:none">
</div>
<p class="note" style="margin:.6rem 0 0;text-align:center">Loading a checkpoint applies it here <b>and</b> pushes it to every connected device — use it to recover the group after a failure.</p>
</div>
<div class="card">
<div class="lbl">❋ Log</div>
<pre id="log"></pre>
</div>
<p class="note">Needs a secure context (localhost or HTTPS) for WebGPU + cross-device WebRTC. No WebGPU? The same verified INT8 units run on CPU — old machines (e.g. via Supermium) still join, just slower. Every training step goes through the Neural Units; there is no plain-float path, and if the units fail to load, training is disabled.</p>
<p class="note"><b>Heads up:</b> peers connect directly (WebRTC), so devices in your group can see each other's IP address, and there's no gradient authentication — only train with devices/people you trust. Proof of concept.</p>
<script src="traincore.js"></script>
<script src="verified_core.js"></script>
<script src="transformer.js"></script>
<script src="webgpu.js"></script>
<script src="app.js"></script>
</body>
</html>
|