MikhailBird's picture
Update DreamZero saved-video explorer
197112d verified
Raw History Blame Contribute Delete
17.5 kB
"use strict";
const $ = id => document.getElementById(id);
const state = {manifest:null,checkpoint:2000,seed:null,detail:null,step:0,decision:0,mode:"paused",load:0,layout:"canvas",chartData:[]};
const cache = new Map();
const sim = $("simVideo"), wam = $("wamVideo");
const clamp = (v, lo, hi) => Math.min(hi, Math.max(lo, v));
const number = value => Number(value).toLocaleString("en-US");
const time = seconds => `${Math.floor(seconds/60)}:${String(Math.floor(seconds%60)).padStart(2,"0")}`;
const asset = name => `${state.world.media}/${name}`;
function toast(message){$("toast").textContent=message;$("toast").classList.add("visible");clearTimeout(toast.timer);toast.timer=setTimeout(()=>$("toast").classList.remove("visible"),2600);}
function fail(error){console.error(error);$("error").hidden=false;$("error").textContent=`The viewer could not load this recording. ${error.message || error}`;}
async function getData(path){if(!cache.has(path)){cache.set(path,fetch(path).then(r=>{if(!r.ok)throw new Error(`Recording data returned ${r.status}.`);return r.json();}).catch(e=>{cache.delete(path);throw e;}));}return cache.get(path);}
function stop(){if(state.animation)cancelAnimationFrame(state.animation);state.mode="paused";sim.pause();wam.pause();$("play").textContent="▶ Play rollout";$("play").setAttribute("aria-label","Play simulator rollout");$("loopPrediction").classList.remove("active");$("loopPrediction").textContent="▶ Loop this forecast";}
function seek(video,target){if(!Number.isFinite(target))return;video.pendingSeek=target;if(video.readyState>=1){const end=Math.max(0,video.duration-.002);const value=clamp(target,0,end);if(Math.abs(video.currentTime-value)>.012){try{video.currentTime=value;}catch(_){}}}}
function url(){const value=new URL(window.location.href);value.searchParams.set("checkpoint",state.checkpoint);value.searchParams.set("world",state.seed);value.searchParams.set("step",Math.round(state.step));if(state.layout!=="canvas")value.searchParams.set("view",state.layout);else value.searchParams.delete("view");return value;}
function saveUrl(){try{history.replaceState(null,"",url());}catch(_){}}
function currentReward(detail,step){let low=0,high=detail.rewards.length-1;while(low<high){const mid=Math.ceil((low+high)/2);if(detail.rewards[mid][0]<=step)low=mid;else high=mid-1;}return detail.rewards[low]?.[1]||0;}
function renderPosition(){if(!state.detail)return;const d=state.detail,anchor=state.decision*d.action_horizon;$("timeline").value=Math.round(state.step);$("stepLabel").innerHTML=`${number(Math.round(state.step))} <span>/ ${number(d.step_budget)} actions</span>`;$("timeLabel").textContent=`${time(state.step*.034)} / ${time(d.step_budget*.034)} simulated`;$("windowBadge").textContent=`FORECAST ${String(state.decision+1).padStart(3,"0")} / ${d.decisions}`;$("predictionNumber").value=state.decision+1;$("forecastLabel").textContent=`Forecast · steps ${number(anchor)}–${number(anchor+d.action_horizon)}`;$("prevPrediction").disabled=state.decision===0;$("nextPrediction").disabled=state.decision===d.decisions-1;$("predictionNumber").max=d.decisions;$("predictionCount").textContent=d.decisions;const last=state.decision===d.decisions-1&&d.steps_executed-anchor<d.action_horizon;$("forecastPosition").innerHTML=last?`Final window <span>· ${d.steps_executed-anchor} actions executed</span>`:"Current frame <span>→ +6 → +12 → … → +48 actions</span>";const x=42+state.step/d.step_budget*(state.plotWidth||1134);const line=$("chartCursor");if(line){line.setAttribute("x1",x);line.setAttribute("x2",x);}updateSampleLabel();}
function simActionsPerSecond(){return state.detail.sim_sample_every*state.detail.sim_video_fps;}
function renderSampling(){const d=state.detail;const dt=state.manifest.control_dt_seconds;const source=d.sim_recording==="verified_action_replay"?"Simulator frames were re-rendered from saved actions":"Simulator frames were recorded";$("samplingNote").innerHTML=`<strong>Actual sampling rates.</strong> ${source} every <strong>${d.sim_sample_every} actions (${(d.sim_sample_every*dt).toFixed(3)} s)</strong>, stored at ${d.sim_video_fps} FPS. WAM predictions contain a frame every <strong>${d.prediction_stride} actions (~${(d.prediction_stride*dt).toFixed(2)} s)</strong>. The simulator holds the last recorded frame between samples.`;$("lastSampleLabel").textContent=number((d.sim_frames-1)*d.sim_sample_every);for(const option of $("speed").options)option.disabled=Number(option.value)/(simActionsPerSecond()*dt)>16;if($("speed").selectedOptions[0].disabled)$("speed").value="4";}
function updateSampleLabel(){if(!state.detail)return;const sample=Math.min(state.detail.sim_frames-1,Math.max(0,Math.floor(sim.currentTime*state.detail.sim_video_fps+.0001)))*state.detail.sim_sample_every;$("sampleLabel").textContent=`Recorded sample · step ${number(sample)}`;}
function setStep(value,{pause=true,seekSim=true,deriveDecision=true,writeUrl=true}={}){if(!state.detail)return;if(pause)stop();state.step=clamp(Number(value)||0,0,state.detail.steps_executed);if(deriveDecision)state.decision=Math.min(state.detail.decisions-1,Math.floor(state.step/state.detail.action_horizon));if(seekSim)seek(sim,state.step/(state.detail.sim_sample_every*state.detail.sim_video_fps));const offset=state.decision*state.detail.prediction_window_seconds;const inWindow=clamp((state.step-state.decision*48)/30,0,1.799);if(state.mode!=="prediction")seek(wam,offset+inWindow+.0001);renderPosition();if(writeUrl)saveUrl();}
function gotoPrediction(value){if(!state.detail)return;stop();state.decision=clamp(Math.round(value),0,state.detail.decisions-1);setStep(state.decision*state.detail.action_horizon,{deriveDecision:false});}
function checkpointColor(step){const colors=["#197b71","#9475b3","#d69a45","#4b78b8"];const index=state.manifest.checkpoints.findIndex(cp=>cp.step===step);return colors[Math.max(index,0)%colors.length];}
function renderWorlds(){const checkpoint=state.manifest.checkpoints.find(cp=>cp.step===state.checkpoint);$("worlds").replaceChildren();checkpoint.worlds.forEach((world,i)=>{const button=document.createElement("button");button.className="world-card";button.setAttribute("aria-pressed",String(world.seed===state.seed));button.setAttribute("aria-label",`World ${world.world_number||i+1}, seed ${world.seed}, maximum reward ${world.max_reward}, ${world.success?"task solved":"task not solved"}`);button.title=`Seed ${world.seed}`;button.innerHTML=`<img src="${world.media}/sim-poster.jpg" alt="Initial scene for world ${world.world_number||i+1}" loading="lazy"><span class="world-card-text"><strong>W${String(world.world_number||i+1).padStart(2,"0")}</strong><small>${world.success?"SOLVED":"NOT SOLVED"}</small></span><div class="world-card-result">Max reward ${world.max_reward.toFixed(2)}</div><div class="world-card-cadence ${world.sim_video_fps===10?"ready":""}">${world.sim_video_fps===10?"10 FPS ready":"2 FPS"}</div>`;button.addEventListener("click",()=>openWorld(world.seed,0).catch(fail));$("worlds").append(button);});$("successStat").innerHTML=`${checkpoint.successes} <small>/ ${checkpoint.worlds.length} worlds</small>`;$("rewardStat").textContent=checkpoint.mean_max_reward.toFixed(2);$("forecastStat").textContent=number(checkpoint.worlds.reduce((sum,w)=>sum+w.decisions,0));$("checkpointProgress").textContent=`${checkpoint.worlds.length} / ${checkpoint.expected_worlds||checkpoint.worlds.length} recordings ready · results shown for ready worlds`;}
function renderChart(){const svg=$("rewardChart"),d=state.detail;const narrow=svg.clientWidth<600,width=narrow?480:1200,font=narrow?12:9;const plotWidth=width-66;state.plotWidth=plotWidth;svg.setAttribute("viewBox",`0 0 ${width} 190`);let html="";const y=r=>14+(1-r)*140;for(let i=0;i<=4;i++){const r=i/4;html+=`<line x1="42" y1="${y(r)}" x2="${width-24}" y2="${y(r)}" stroke="#e7ede7" stroke-dasharray="${i?"3 5":"0"}"/><text x="30" y="${y(r)+3}" text-anchor="end" fill="#7f8d7c" font-size="${font}" font-family="monospace">${Math.round(r*100)}%</text>`;}for(const step of [0,5500,11000,16500,22000]){html+=`<text x="${42+step/22000*plotWidth}" y="179" text-anchor="middle" fill="#89967e" font-size="${font}" font-family="monospace">${number(step)}</text>`;}const sorted=[...state.chartData].sort((a,b)=>(a.checkpoint===state.checkpoint)-(b.checkpoint===state.checkpoint));for(const detail of sorted){const path=detail.rewards.map((r,i)=>`${i?"L":"M"}${(42+r[0]/d.step_budget*plotWidth).toFixed(2)},${y(r[1]).toFixed(2)}`).join(" ");const active=detail.checkpoint===state.checkpoint;html+=`<path d="${path}" fill="none" stroke="${checkpointColor(detail.checkpoint)}" stroke-width="${active?2.7:1.7}" opacity="${active?1:.65}"/>`;}html+='<line id="chartCursor" x1="42" y1="8" x2="42" y2="160" stroke="#263f30" stroke-width="1" stroke-dasharray="3 3"/>';svg.innerHTML=html;renderPosition();}
async function openWorld(seed,step=0){const version=++state.load;stop();$("error").hidden=true;state.seed=Number(seed);const cp=state.manifest.checkpoints.find(x=>x.step===state.checkpoint);state.world=cp.worlds.find(x=>x.seed===state.seed)||cp.worlds[0];state.seed=state.world.seed;renderWorlds();const [detail,...others]=await Promise.all([getData(state.world.data),...state.manifest.checkpoints.filter(x=>x.step!==state.checkpoint).map(x=>{const w=x.worlds.find(x=>x.seed===state.seed);return w?getData(w.data).catch(()=>null):null;})]);if(version!==state.load)return;state.detail=detail;renderSampling();state.chartData=[detail,...others.filter(Boolean)];state.step=step;state.decision=Math.min(detail.decisions-1,Math.floor(step/48));const index=cp.worlds.findIndex(x=>x.seed===state.seed);$("worldNumber").textContent=String(state.world.world_number||index+1).padStart(2,"0");$("seedLabel").textContent=state.seed;$("worldMax").textContent=detail.max_reward.toFixed(2);$("worldFinal").textContent=detail.final_reward.toFixed(2);$("outcomeBadge").textContent=detail.success?"Task solved":"Budget reached · not solved";$("outcomeBadge").classList.toggle("success",detail.success);$("simDownload").href=asset("rollout-strip.mp4");$("wamDownload").href=asset("predictions.mp4");$("dataDownload").href=state.world.data;$("timeline").max=detail.steps_executed;$("firstReward").disabled=!detail.rewards.some(r=>r[1]>0);sim.poster=asset("sim-poster.jpg");wam.poster=asset("wam-poster.jpg");$("simLoading").classList.remove("hidden");$("wamLoading").classList.remove("hidden");sim.src=asset(state.layout==="strip"?"rollout-strip.mp4":"rollout-canvas.mp4");wam.src=asset("predictions.mp4");sim.load();wam.load();renderChart();setStep(step);document.title=`World ${state.world.world_number||index+1} · Step ${number(state.checkpoint)} · DreamZero Rollout Lab`;}
async function toggleRollout(){if(!state.detail)return;if(state.mode==="rollout"){stop();saveUrl();return;}stop();if(state.step>=state.detail.steps_executed-state.detail.sim_sample_every)setStep(0);state.mode="rollout";sim.playbackRate=Number($("speed").value)/(simActionsPerSecond()*.034);$("play").textContent="❚❚ Pause rollout";$("play").setAttribute("aria-label","Pause simulator rollout");try{await sim.play();}catch(e){stop();toast("Press play again once the video has loaded.");}}
function guardPredictionLoop(){if(state.mode!=="prediction"||!state.detail)return;const start=state.decision*1.8;if(wam.readyState>=2&&!wam.seeking&&(wam.currentTime>=start+1.79||wam.currentTime<start-.02))seek(wam,start+.0001);state.animation=requestAnimationFrame(guardPredictionLoop);}
async function loopPrediction(){if(!state.detail)return;if(state.mode==="prediction"){stop();return;}stop();state.mode="prediction";seek(wam,state.decision*1.8+.0001);wam.playbackRate=1;$("loopPrediction").textContent="❚❚ Pause forecast";$("loopPrediction").classList.add("active");guardPredictionLoop();try{await wam.play();}catch(e){stop();toast("The prediction is loading. Please try again.");}}
for(const [video,label] of [[sim,"simLoading"],[wam,"wamLoading"]]){video.addEventListener("loadedmetadata",()=>{if(video.pendingSeek!==undefined)seek(video,video.pendingSeek);});for(const event of ["loadeddata","canplay","seeked"]){video.addEventListener(event,()=>{$(label).classList.add("hidden");if(video===sim)updateSampleLabel();});}video.addEventListener("waiting",()=>{$(label).textContent="Buffering video…";$(label).classList.remove("hidden");});video.addEventListener("error",()=>{if(video.error&&video.getAttribute("src")){ $(label).textContent="Video could not load. Try its download link.";$(label).classList.remove("hidden");}});}
sim.addEventListener("timeupdate",()=>{if(state.mode==="rollout")setStep(sim.currentTime*simActionsPerSecond(),{pause:false,seekSim:false,writeUrl:false});else updateSampleLabel();});
sim.addEventListener("ended",()=>{if(state.mode==="rollout"){stop();setStep(state.detail.steps_executed);}});
wam.addEventListener("timeupdate",()=>{if(state.mode!=="prediction"||!state.detail)return;const start=state.decision*1.8;const relative=wam.currentTime-start;if(relative>=1.775){seek(wam,start+.0001);return;}if(relative<-.02){seek(wam,start+.0001);return;}state.step=Math.min(state.detail.steps_executed,state.decision*48+Math.min(48,Math.max(0,relative)*30));seek(sim,state.step/simActionsPerSecond());renderPosition();});
wam.addEventListener("ended",()=>{if(state.mode==="prediction"){seek(wam,state.decision*1.8+.0001);wam.play().catch(()=>stop());}});
$("play").addEventListener("click",toggleRollout);$("loopPrediction").addEventListener("click",loopPrediction);$("restart").addEventListener("click",()=>setStep(0));$("timeline").addEventListener("input",e=>setStep(e.target.value));$("speed").addEventListener("change",()=>{if(!state.detail)return;sim.playbackRate=Number($("speed").value)/(simActionsPerSecond()*.034);});$("prevPrediction").addEventListener("click",()=>gotoPrediction(state.decision-1));$("nextPrediction").addEventListener("click",()=>gotoPrediction(state.decision+1));$("predictionNumber").addEventListener("change",e=>gotoPrediction(Number(e.target.value)-1));$("firstReward").addEventListener("click",()=>{const first=state.detail.rewards.find(r=>r[1]>0);if(first)setStep(first[0]);});
$("checkpoint").addEventListener("change",e=>{const step=state.step;state.checkpoint=Number(e.target.value);openWorld(state.seed,step).catch(fail);});
$("layout").addEventListener("change",e=>{stop();state.layout=e.target.value;$("simLoading").classList.remove("hidden");sim.src=asset(state.layout==="strip"?"rollout-strip.mp4":"rollout-canvas.mp4");seek(sim,state.step/simActionsPerSecond());sim.load();saveUrl();});
$("rewardChart").addEventListener("pointerdown",e=>{if(!state.detail)return;const rect=e.currentTarget.getBoundingClientRect();const width=e.currentTarget.viewBox.baseVal.width;const x=(e.clientX-rect.left)/rect.width*width;setStep((x-42)/(width-66)*state.detail.step_budget);});
$("share").addEventListener("click",async()=>{const link=url().href;try{await navigator.clipboard.writeText(link);toast("Link copied to this world and step.");}catch(_){window.prompt("Copy this recording link:",link);}});
document.addEventListener("keydown",e=>{if(e.target.matches("input,select,textarea,button")||!state.detail)return;if(e.code==="Space"){e.preventDefault();toggleRollout();}else if(e.code==="ArrowRight"){e.preventDefault();gotoPrediction(state.decision+1);}else if(e.code==="ArrowLeft"){e.preventDefault();gotoPrediction(state.decision-1);}});
window.addEventListener("pagehide",stop);let resizeTimer;window.addEventListener("resize",()=>{clearTimeout(resizeTimer);resizeTimer=setTimeout(()=>{if(state.detail)renderChart();},150);});
async function init(){state.manifest=await getData("manifest.json");const params=new URLSearchParams(location.search);const catalog=state.manifest.checkpoint_catalog||state.manifest.checkpoints.map(cp=>({step:cp.step,label:`Step ${number(cp.step)}`,ready_worlds:cp.worlds.length,expected_worlds:cp.worlds.length}));$("checkpoint").replaceChildren();[...catalog].sort((a,b)=>b.step-a.step).forEach(cp=>{const option=document.createElement("option");option.value=cp.step;option.textContent=cp.label+(cp.ready_worlds===0?" · preparing videos":"");option.disabled=cp.ready_worlds===0;$("checkpoint").append(option);});$("chartLegend").replaceChildren();state.manifest.checkpoints.forEach(cp=>{const item=document.createElement("span");const line=document.createElement("i");line.className="legend-line";line.style.background=checkpointColor(cp.step);item.append(line,document.createTextNode(`Step ${number(cp.step)}`));$("chartLegend").append(item);});const candidate=Number(params.get("checkpoint")||state.manifest.default_checkpoint||2000);state.checkpoint=state.manifest.checkpoints.some(x=>x.step===candidate)?candidate:state.manifest.checkpoints[0].step;$("checkpoint").value=state.checkpoint;const cp=state.manifest.checkpoints.find(x=>x.step===state.checkpoint);const requested=Number(params.get("world"));const seed=cp.worlds.some(w=>w.seed===requested)?requested:cp.worlds[0].seed;state.layout=params.get("view")==="strip"?"strip":"canvas";$("layout").value=state.layout;$("buildLabel").textContent=`${state.manifest.checkpoints.flatMap(cp=>cp.worlds).filter(world=>world.sim_video_fps===10).length} / ${state.manifest.expected_total_worlds||state.manifest.checkpoints.flatMap(cp=>cp.worlds).length} rollouts at 10 FPS · ${number(state.manifest.total_prediction_windows)} forecast windows`;await openWorld(seed,clamp(Number(params.get("step"))||0,0,state.manifest.step_budget));}
init().catch(fail);