Spaces:
Running
Running
Download app.js from nvidia/synthda-demo: direct link, hf CLI and curl.
- Browser
- Download file 16.3 kB
-
https://huggingface.co/spaces/nvidia/synthda-demo/resolve/main/app.js
- Command line
-
hf download hf://spaces/nvidia/synthda-demo/app.js
-
curl -L -o app.js https://huggingface.co/spaces/nvidia/synthda-demo/resolve/main/app.js
16.3 kB
| import {smoothMotion} from './smoothing.js'; | |
| import {COLORS,validateMotion,validateMeshes,boundsFor,renderStage} from './motion.js'; | |
| const $=s=>document.querySelector(s),state={manifest:null,example:null,data:null,raw:null,meshes:null,frame:0,variant:5,yaw:20,zoom:1,mode:'compare',speed:1,playing:false,last:0,request:0,bounds:null}; | |
| const cache=new Map(),smoothCache=new Map();let loadController=null,animation=null,archiveTimer=null; | |
| let soma=null,somaViews=null,somaRequest=0,somaMotions=new Map(),somaInit=null; | |
| async function ensureSoma(){ | |
| const request=++somaRequest,id=state.example?.id;if(!id)return; | |
| if(typeof WebGL2RenderingContext==='undefined'){ $('#soma-status').textContent='SOMA needs WebGL 2. Showing the saved skeletons on this device.';return;} | |
| $('#soma-status').textContent='Loading fitted SOMA bodies…'; | |
| try{ | |
| somaInit??=import('./soma-view.js').then(async m=>{soma=await m.loadSoma();somaViews={pipeline:new m.SomaView($('#pipeline-soma'),soma.rig),playground:new m.SomaView($('#motion-soma'),soma.rig)};});await somaInit; | |
| const keys=['sourceA','sourceB',...Array.from({length:9},(_,i)=>String(i+1))];await soma.ensure(id,keys); | |
| const loaded=await Promise.all(keys.map(async k=>[k,await soma.motion(id,k)]));if(request!==somaRequest||id!==state.example.id)return; | |
| somaMotions=new Map(loaded);$('#soma-status').textContent='SOMA body fitted to saved poses · use Show pose joints to inspect the fit';draw(); | |
| }catch(error){if(request!==somaRequest)return;if(!somaViews)somaInit=null;somaMotions.clear();$('#soma-status').textContent='SOMA could not load. The saved skeletons remain available; reselect an example to retry.';console.error(error);draw();} | |
| } | |
| function status(message,error=false){$('#load-status').textContent=message;$('#load-status').classList.toggle('error',error);} | |
| async function getJSON(path,signal){const response=await fetch(path,{signal});if(!response.ok)throw Error(`Could not load ${path} (${response.status})`);return response.json();} | |
| function textElement(tag,text,className){const node=document.createElement(tag);node.textContent=text;if(className)node.className=className;return node;} | |
| function parameter(){return `wA ${(state.variant/10).toFixed(1)} · wB ${((10-state.variant)/10).toFixed(1)}`;} | |
| function updateLabels(){if(!state.data)return;const box=$('#motion-labels');box.replaceChildren();let labels; | |
| if(state.mode==='lineup')labels=[1,3,5,7,9].map(v=>({label:`wA ${(v/10).toFixed(1)}`,sub:'Saved output',color:COLORS.output})); | |
| else labels=[{label:state.example.sourceA.label,sub:'Source A',color:COLORS.real},{label:'SynthDA variant',sub:parameter(),color:COLORS.output},{label:state.example.sourceB.label,sub:'Source B',color:COLORS[state.example.sourceB.kind]}]; | |
| box.style.gridTemplateColumns=`repeat(${labels.length},minmax(0,1fr))`; | |
| for(const l of labels){const node=document.createElement('div'),swatch=textElement('span','','swatch');swatch.style.background=l.color;node.append(swatch,document.createTextNode(l.label),textElement('small',l.sub));box.append(node);} | |
| $('#variant').disabled=state.mode==='lineup';$('#variant-note').textContent=state.mode==='lineup'?'Five fixed saved variants are shown together. Switch to Compare motions or Overlay poses to select an individual variant.':'The slider selects a saved optimizer output. Its parameters are not percentages of an endpoint crossfade.'; | |
| $('#variant-value').textContent=parameter();$('#pipeline-variant').textContent='Same extracted pose · fitted SOMA body'; | |
| } | |
| function entries(){const d=state.data;if(state.mode==='lineup')return [1,3,5,7,9].map(i=>({key:String(i),sequence:d.variants[i],color:COLORS.output}));return[{key:'sourceA',sequence:d.sourceA,color:COLORS.real},{key:'variant',sequence:d.variants[state.variant],color:COLORS.output},{key:'sourceB',sequence:d.sourceB,color:COLORS[state.example.sourceB.kind]}];} | |
| function draw(){if(!state.data)return; | |
| const list=entries(),source=[{key:'sourceA',sequence:state.data.sourceA,color:COLORS.real}],ready=somaViews&&somaMotions.has('sourceA'),body=ready&&$('#body-view').value==='soma',human=ready&&$('#pipeline-human').checked; | |
| $('#pipeline-variant').textContent=human?'Same extracted pose · fitted SOMA body':'Same extracted pose · skeleton preview'; | |
| renderStage($('#motion-canvas'),list,state.data.parents,state.frame,state.yaw,state.zoom,state.bounds,{overlay:state.mode==='overlay',skeleton:!body}); | |
| renderStage($('#pipeline-pose'),source,state.data.parents,state.frame,state.yaw,1,state.bounds); | |
| renderStage($('#pipeline-output'),source,state.data.parents,state.frame,state.yaw,1,state.bounds,{skeleton:!human}); | |
| if(somaViews){ | |
| const options={parents:state.data.parents,smoothed:$('#smoothing').value!=='original'}; | |
| if(body)somaViews.playground.draw(list.map(e=>({...e,motion:somaMotions.get(e.key==='variant'?String(state.variant):e.key)})),state.frame,state.yaw,state.zoom,state.bounds,{...options,overlay:state.mode==='overlay',showJoints:$('#body-joints').checked});else somaViews.playground.clear(); | |
| if(human)somaViews.pipeline.draw(source.map(e=>({...e,motion:somaMotions.get('sourceA')})),state.frame,state.yaw,1,state.bounds,{...options,showJoints:$('#pipeline-skeleton').checked});else somaViews.pipeline.clear(); | |
| } | |
| } | |
| function syncVideo(){const v=$('#pipeline-video');if(!state.example||!Number.isFinite(v.duration)||v.duration<=0||v.seeking)return;const target=state.frame/(state.example.frames-1)*Math.max(0,v.duration-.04);if(Math.abs(v.currentTime-target)>.045)v.currentTime=target;} | |
| function showFrame(){if(!state.example)return;for(const id of ['#frame','#pipeline-frame'])$(id).value=Math.floor(state.frame);const text=`Frame ${Math.floor(state.frame)+1} / ${state.example.frames}`;$('#frame-count').textContent=text;$('#pipeline-count').textContent=text;draw();syncVideo();} | |
| function stop(){state.playing=false;state.last=0;if(animation)cancelAnimationFrame(animation);animation=null;$('#play').textContent='Play';$('#pipeline-play').textContent='Play sequence';} | |
| function tick(t){if(!state.playing)return;if(!state.last)state.last=t;const dt=1000/(state.example.previewFps*state.speed),step=(t-state.last)/dt;state.last=t;if(step){state.frame=Math.min(state.example.frames-1,state.frame+step);showFrame();}if(state.frame>=state.example.frames-1){stop();return;}animation=requestAnimationFrame(tick);} | |
| function play(){if(!state.data)return;if(state.playing){stop();return;}if(state.frame>=state.example.frames-1)state.frame=0;state.playing=true;state.last=0;$('#play').textContent='Pause';$('#pipeline-play').textContent='Pause sequence';animation=requestAnimationFrame(tick);} | |
| function controlsEnabled(enabled){for(const id of ['#play','#pipeline-play','#frame','#pipeline-frame','#download-pose'])$(id).disabled=!enabled;} | |
| async function selectExample(id){const example=state.manifest.examples.find(e=>e.id===id);if(!example)return;stop();$('#soma-status').textContent='Loading fitted SOMA bodies…';somaRequest++;somaMotions.clear();somaViews?.pipeline.clear();somaViews?.playground.clear();controlsEnabled(false);loadController?.abort();loadController=new AbortController();const {signal}=loadController,request=++state.request;status('Loading motion…'); | |
| try{let data=cache.get(id);if(!data){data=validateMotion(await getJSON(example.data,signal),example.frames);cache.set(id,data);}if(request!==state.request)return;state.example=example;state.raw=data;if(!smoothCache.has(id))smoothCache.set(id,smoothMotion(data));state.data=$('#smoothing').value==='original'?data:smoothCache.get(id);state.frame=0;state.meshes=null;state.bounds=boundsFor(data,state.yaw);$('#example').value=id;$('#action-label').textContent=`${example.action} · ${$('#smoothing').value==='original'?'original poses':'smoothed preview'}`;$('#prompt').textContent=example.prompt?`Prompt: “${example.prompt}”`:'Two real video-derived motions';$('#pipeline-source').textContent=example.sourceA.label;$('#pipeline-video').src=example.sourceA.video;$('#video-error').textContent='';for(const sel of ['#frame','#pipeline-frame'])$(sel).max=example.frames-1;$('#show-mesh').checked=false;$('.mesh-control').hidden=true;$('#mesh-note').textContent='Native SOMA-X neutral bodies are fitted to the saved joints, with neutral fingers and fixed proportions. They approximate the motion; they are not recovered subject geometry. Smoothing affects only the preview, and downloads retain the original saved poses.'; | |
| const p=example.provenance;$('#provenance').replaceChildren(textElement('p',`Source: ${p.repository} · revision ${p.revision}`),textElement('p',`Example: ${p.folder}`),textElement('p',`Source A: ${p.files.sourceA}; source B: ${p.files.sourceB}.`)); | |
| document.querySelectorAll('.example-card').forEach(b=>{b.classList.toggle('selected',b.dataset.example===id);b.setAttribute('aria-pressed',String(b.dataset.example===id));});updateLabels();showFrame();controlsEnabled(true);status('');ensureSoma(); | |
| if(example.meshes){try{const meshes=validateMeshes(await getJSON(example.meshes,signal),example.frames);if(request!==state.request)return;state.meshes=meshes;$('.mesh-control').hidden=false;$('#mesh-note').textContent='Matching mesh frames are available in the same coordinate system. Toggle Mesh overlay to inspect the surface and skeleton together.';}catch(error){if(error.name!=='AbortError'&&request===state.request)$('#mesh-note').textContent='Mesh data could not be loaded. The verified skeleton data remains available.';}} | |
| }catch(error){if(error.name==='AbortError')return;if(request===state.request){status('This example could not be loaded. Choose another example or reload the page.',true);console.error(error);}} | |
| } | |
| function archiveUpdate(){const set=state.manifest.archive.find(e=>e.id===$('#archive-set').value);if(!set)return;const value=Number($('#archive-variant').value),path=set.clips[value];$('#archive-value').textContent=(value/10).toFixed(1);$('#archive-title').textContent=set.title;$('#archive-status').textContent='';$('#archive-video').pause();$('#archive-video').src=path;$('#archive-download').href=path;} | |
| function renderResults(){const rows=state.manifest.examples.filter(e=>e.recognition&&e.recognition.source&&Number.isFinite(e.recognition.baseline)&&Number.isFinite(e.recognition.augmented));if(!rows.length)return;const target=$('#recognition-results');target.hidden=false;target.replaceChildren(textElement('h3','Reported recognition results'));const table=document.createElement('table');table.className='results-table';const head=document.createElement('tr');for(const label of ['Example / evaluation','Baseline','With augmentation','Source'])head.append(textElement('th',label));table.append(head);for(const e of rows){const r=e.recognition,tr=document.createElement('tr');tr.append(textElement('td',`${e.title} · ${r.dataset} · ${r.metric}`),textElement('td',`${r.baseline} ${r.unit}`),textElement('td',`${r.augmented} ${r.unit}`));const td=document.createElement('td'),a=textElement('a','Evaluation details ↗');a.href=r.source;a.target='_blank';a.rel='noopener';td.append(a);tr.append(td);table.append(tr);}target.append(table);} | |
| async function init(){try{state.manifest=await getJSON('assets/manifest.json');const select=$('#example');select.replaceChildren();for(const e of state.manifest.examples){const opt=textElement('option',e.title);opt.value=e.id;select.append(opt);const card=textElement('button','','example-card');card.type='button';card.dataset.example=e.id;card.setAttribute('aria-pressed','false');card.append(textElement('small',e.action),textElement('strong',e.title));const meta=textElement('span','','card-meta');meta.append(textElement('small','9 saved variants'),textElement('small','Explore →'));card.append(meta);card.onclick=()=>{selectExample(e.id);$('#playground').scrollIntoView({behavior:matchMedia('(prefers-reduced-motion:reduce)').matches?'instant':'smooth'});};$('#example-cards').append(card);}select.disabled=false;select.onchange=()=>selectExample(select.value);const classes=[...new Set(state.manifest.examples.map(e=>e.action))];$('#gallery-scope').textContent=`${classes.join(' · ')} · ${state.manifest.examples.length} verified pose examples`;for(const set of state.manifest.archive){const opt=textElement('option',set.title);opt.value=set.id;$('#archive-set').append(opt);}$('#archive-set').disabled=false;archiveUpdate();renderResults();await selectExample(state.manifest.examples[0].id);}catch(error){status('The demo could not load. Check your connection and reload. For a local preview, serve this folder over HTTP.',true);console.error(error);}} | |
| $('#play').onclick=play;$('#pipeline-play').onclick=play;for(const id of ['#frame','#pipeline-frame'])$(id).oninput=e=>{stop();state.frame=Number(e.target.value);showFrame();};$('#variant').oninput=e=>{state.variant=Number(e.target.value);updateLabels();draw();};function setYaw(value){state.yaw=Math.max(-180,Math.min(180,Math.round(value)));$('#camera').value=state.yaw;$('#camera-value').textContent=`${state.yaw}°`;if(state.raw)state.bounds=boundsFor(state.raw,state.yaw);draw();}$('#camera').oninput=e=>setYaw(Number(e.target.value));function setZoom(value){state.zoom=Math.max(.6,Math.min(1.6,value));$('#zoom').value=state.zoom;$('#zoom-value').textContent=`${Number(state.zoom.toFixed(2))}×`;draw();}$('#zoom').oninput=e=>setZoom(Number(e.target.value));$('#speed').onchange=e=>{state.speed=Number(e.target.value);state.last=0;};$('#show-mesh').onchange=draw;document.querySelectorAll('[data-mode]').forEach(b=>b.onclick=()=>{state.mode=b.dataset.mode;document.querySelectorAll('[data-mode]').forEach(x=>x.setAttribute('aria-pressed',String(x===b)));updateLabels();draw();});$('#archive-set').onchange=archiveUpdate;$('#archive-variant').oninput=()=>{clearTimeout(archiveTimer);$('#archive-value').textContent=(Number($('#archive-variant').value)/10).toFixed(1);archiveTimer=setTimeout(archiveUpdate,150);};$('#archive-video').onerror=()=>{$('#archive-status').textContent='This clip could not be played. Try downloading it or choose another clip.';};$('#pipeline-video').onloadedmetadata=syncVideo;$('#pipeline-video').onseeked=syncVideo;$('#pipeline-video').onerror=()=>{$('#video-error').textContent='The original video could not be played. The pose views remain available.';}; | |
| $('#download-pose').onclick=()=>{if(!state.data)return;const payload={example:state.example.id,action:state.example.action,previewFps:state.example.previewFps,weights:{wA:state.variant/10,wB:(10-state.variant)/10},coordinates:'original repository coordinates; root-relative; units not independently calibrated',parents:state.data.parents,poses:state.raw.variants[state.variant],provenance:state.example.provenance};const url=URL.createObjectURL(new Blob([JSON.stringify(payload)],{type:'application/json'})),a=document.createElement('a');a.href=url;a.download=`${state.example.id}-wA${(state.variant/10).toFixed(1)}.json`;a.click();setTimeout(()=>URL.revokeObjectURL(url),1000);}; | |
| let dragging=null;$('#motion-canvas').onpointerdown=e=>{dragging={x:e.clientX,yaw:state.yaw};e.currentTarget.setPointerCapture(e.pointerId);};$('#motion-canvas').onpointermove=e=>{if(dragging)setYaw(dragging.yaw+(e.clientX-dragging.x)*.5);};for(const event of ['pointerup','pointercancel','lostpointercapture'])$('#motion-canvas').addEventListener(event,()=>dragging=null);$('#motion-canvas').addEventListener('wheel',e=>{if(!state.data)return;e.preventDefault();setZoom(state.zoom-e.deltaY*.001);},{passive:false});new ResizeObserver(draw).observe($('#motion-canvas'));new ResizeObserver(draw).observe($('#pipeline-pose'));document.addEventListener('visibilitychange',()=>{if(document.hidden)stop();});window.addEventListener('pagehide',stop);init(); | |
| $('#pipeline-human').onchange=draw;$('#pipeline-skeleton').onchange=draw;$('#smoothing').onchange=()=>{if(!state.raw)return;state.data=$('#smoothing').value==='original'?state.raw:smoothCache.get(state.example.id);$('#smoothing-note').textContent=$('#smoothing').value==='original'?'Unfiltered saved poses':'Temporal smoothing for display · original data preserved';$('#action-label').textContent=state.example.action+($('#smoothing').value==='original'?' · original poses':' · smoothed preview');draw();}; | |
| $('#body-view').onchange=draw;$('#body-joints').onchange=draw; | |