File size: 14,175 Bytes
771f63f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import {escape} from './common.js';

const tutorials={
 crossy:{name:'Crossy Road RL',steps:[
  {title:'Teach a chicken through trial and error',target:'.arcade-rl-stage',text:'Your goal is to help an AI cross traffic. Chickens try moves, receive rewards for progress and penalties for collisions, and share what they learn. This illustrates learning from consequences.'},
  {title:'Try one tiny step',target:'#rl-step',text:'Click the highlighted One tick button. Traffic advances by one small physics step. Actions and their consequences build experience; one tick alone will not make a skilled player.',watch:'#rl-step',event:'click',reveal:'.arcade-rl-stage',success:'You advanced the real game. Watch the traffic and experience counters as you practice.'},
  {title:'See what information the AI gets',target:'.rl-input-panel',text:'These are actual inputs: the chicken’s location and nearby cars’ positions, speeds, and lengths. The learner chooses from Wait, Forward, Left, Right, and Back. It receives coordinates rather than learning from screenshots.'},
  {title:'Practice, then compare',target:'#rl-start',text:'Use Start / resume learning to practice and Pause to stop. Then Test on 4 new layouts checks the learned choices with randomness and learning switched off. Compare average rows reached before and after practice.',watch:'#rl-start',event:'click',success:'Practice has started. Pause whenever you want to inspect or test the learner.'},
  {title:'What would count as improvement?',target:'#rl-test',text:'A lucky chicken can have one great run. A separate test helps you check whether the learned choices work on other roads.',question:{answers:['Higher average distance on the same set of test roads','One chicken gets lucky once'],correct:0,feedback:'Compare average distance on the same test layouts after similar amounts of practice. Improvement is something to measure.'}}
 ]},
 pictionary:{name:'Neural Pictionary',steps:[
  {title:'Explore how a drawing becomes a guess',target:'.paper',text:'Draw an object and the AI proposes names from the image. Add details or a hint to see its interpretation change. The goal is to explore visual evidence and context, rather than pick from a fixed word list.'},
  {title:'Try a tiny practice sketch',target:'.paper',text:'A circle could be a ball, a coin, or the sun. Add a detail below and consider how it changes your interpretation. This practice illustration stays in the tutorial and makes no model request.',exercise:'sketch'},
  {title:'Give the drawing some context',target:'#hint',text:'Try typing a useful hint in the highlighted box. In the activity, the arrow sends your drawing and hint for a new guess. Drawing on the main canvas also starts recognition after a short pause.',watch:'#hint',event:'input',success:'You added context. When you want a live guess, use the arrow beside the hint. Drawing pixels and submitted hints go to the AI service.'},
  {title:'Look inside the visual explanation',target:'#feature-scroll',text:'These tiles show fixed filters applied to your drawing: edges, contrasts, and other patterns. They illustrate stages of image processing. The candidate names and output scores come from the actual model responses.'},
  {title:'Check what the picture tells you',target:'.predictions',text:'A model can change its mind as you add evidence. Its guess and scores can still be wrong.',question:{answers:['The feature tiles illustrate filters, not the remote AI’s hidden neurons','Every colored tile is a recording of the remote AI’s neurons'],correct:0,feedback:'The tiles are an illustration built from your drawing. The guesses are actual API results; the remote models’ internal activations are unavailable.'}}
 ]},
 brain:{name:'3D Brain Explorer',steps:[
  {title:'Meet the parts of the brain',target:'.brain-stage',text:'Explore where brain structures sit and what they help do. Drag the model to rotate it, or use its arrow keys. Functions such as memory and movement depend on connected systems, not one part working alone.',watch:'#brain-3d',event:'pointerup',success:'You interacted with the model. You can rotate it, zoom, and select colored regions.'},
  {title:'Open it up',target:'[data-brain-mode="inside"]',text:'Click Look inside to reveal the deeper structures. Outside, Half brain, and Split apart give you other ways to explore their positions.',watch:'[data-brain-mode="inside"]',event:'click',reveal:'.brain-stage',success:'The brain is open. You can see how deeper structures sit beneath the outer cortex.'},
  {title:'Pick a part and read its role',target:'[data-brain-part="occipital"]',text:'Select Occipital lobes in the highlighted list. These lobes help process visual information. The explorer highlights their location and explains their role below the model.',watch:'[data-brain-part="occipital"]',event:'click',reveal:'.brain-stage',success:'You selected the occipital lobes, which help process visual information. Their location is now highlighted in the explorer.'},
  {title:'Follow a connection',target:'#brain-route-start',text:'Press Follow this route, then Next step to trace See → decide → move. The moving dot illustrates connections between teaching landmarks; it is not recorded brain activity.',watch:'#brain-route-start',event:'click',reveal:'.brain-stage',success:'The illustrated route is active. Use Next step below the model to read what each stop contributes.'},
  {title:'What is this explorer meant to teach?',target:'#brain-quiz-start',text:'Use New challenge to test whether you can find a structure from its role. You can replay this tutorial whenever you want.',question:{answers:['Where structures sit and how connected regions contribute to behavior','That every behavior has one isolated brain part'],correct:0,feedback:'Anatomy gives you landmarks. Behavior depends on interacting networks, and the internal shapes and routes here are simplified teaching guides.'}}
 ]},
 cinema:{name:'Neural Cinema',steps:[
  {title:'Compare a film with a predicted brain response',target:'.simple-cinema',text:'The film is the input. TRIBE predicts patterns of brain responses associated with its pictures and sound. These maps were prepared in advance for an average brain; they do not measure you or read your feelings.'},
  {title:'Choose a moment to inspect',target:'#cinema-moment',text:'Move the highlighted slider to a different moment. Compare what is happening in the film with the colored brain map. Use Watch when you want to play the clip with its original soundtrack.',watch:'#cinema-moment',event:'input',success:'You changed the movie moment. Compare the scene and the saved prediction at that time.'},
  {title:'Make sense of the colors',target:'#cinema-legend',text:'Read the response legend: colors show lower or higher predicted response on a shared scale. A brighter region does not identify an emotion or show that this area is the only one involved.'},
  {title:'Try changing what you hear',target:'#cinema-silent',text:'Try Mute playback, then Play sound. These buttons change what you hear while watching. The saved model prediction still uses the original picture-and-sound input; muting does not recalculate it.',watch:'#cinema-silent',event:'click',success:'Playback is muted. The saved prediction is unchanged. Play sound restores the soundtrack.'},
  {title:'What are you seeing?',target:'#cinema-context',text:'Choose another thumbnail below to compare clips. Mood labels help organize the collection; the model did not measure those emotions.',question:{answers:['A model’s saved prediction of an average brain response to this clip','A live measurement of my own emotions'],correct:0,feedback:'The model predicts brain responses from the film. These are saved predictions for an average subject, not measurements of a visitor.'}}
 ]}
};

export function mountActivityTutorial(root,id){
 const tutorial=tutorials[id];if(!tutorial)return()=>{};
 const launcher=document.createElement('div');
 launcher.className='tutorial-launcher';
 launcher.innerHTML=`<div><strong>New here?</strong><span>Take a short tour and try the controls.</span></div><button class="secondary" type="button">Show tutorial</button>`;
 root.prepend(launcher);
 const replay=launcher.querySelector('button');
 let panel=null,target=null,spacer=null,index=0,disposed=false,stepAbort=null,startFrame;
 const answers=new Map(),tried=new Set();let sketchDetailed=false;
 const onKey=event=>{if(event.key==='Escape'&&panel){event.preventDefault();event.stopImmediatePropagation();close(true)}};

 function unhighlight(){stepAbort?.abort();stepAbort=null;target?.classList.remove('tutorial-target');target=null}
 function close(restoreFocus=false){
  unhighlight();panel?.remove();panel=null;spacer?.remove();spacer=null;
  document.body.classList.remove('tutorial-active');document.removeEventListener('keydown',onKey,true);
  replay.textContent='Replay tutorial';replay.setAttribute('aria-expanded','false');
  if(restoreFocus&&root.isConnected)replay.focus({preventScroll:true});
 }
 function feedback(text){const box=panel?.querySelector('.tutorial-feedback');if(box){box.hidden=false;box.textContent=text}}
 function render(){
  if(!panel||disposed||!root.isConnected)return;
  unhighlight();const step=tutorial.steps[index];
  panel.innerHTML=`<div class="tutorial-panel-top"><span>${escape(tutorial.name)} · ${index+1} of ${tutorial.steps.length}</span><button type="button" class="tutorial-skip">Skip tutorial <span aria-hidden="true">×</span></button></div><div class="tutorial-progress" aria-hidden="true">${tutorial.steps.map((_,i)=>`<span class="${i<=index?'is-reached':''}"></span>`).join('')}</div><h2 id="interactive-tutorial-title" tabindex="-1">${escape(step.title)}</h2><p class="tutorial-description">${escape(step.text)}</p>${step.exercise==='sketch'?`<div class="tutorial-practice"><span>Practice illustration</span><svg viewBox="0 0 180 80" role="img" aria-label="${sketchDetailed?'A circle with sun rays':'A simple circle'}"><g fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round"><circle cx="90" cy="40" r="20"/>${sketchDetailed?'<path d="M90 6v7m0 54v7M56 40h7m54 0h7M66 16l5 5m38 38 5 5m0-48-5 5M71 59l-5 5"/>':''}</g></svg><button class="secondary" type="button" data-tutorial-sketch>${sketchDetailed?'Remove the rays':'Add sun rays'}</button><p>${sketchDetailed?'The rays make a sun interpretation more plausible. A useful detail reduces ambiguity.':'What could this circle be? Add a detail and compare.'}</p></div>`:''}${step.question?`<div class="tutorial-answers" aria-label="Quick check">${step.question.answers.map((answer,i)=>`<button class="secondary" type="button" data-tutorial-answer="${i}" aria-pressed="${answers.get(index)===i}">${escape(answer)}</button>`).join('')}</div>`:''}<p class="tutorial-feedback" role="status" hidden></p><div class="tutorial-panel-bottom"><button class="secondary" type="button" data-tutorial-back ${index===0?'disabled':''}>Back</button><span>You can try things or just continue.</span><button class="primary" type="button" data-tutorial-next>${index===tutorial.steps.length-1?'Finish tutorial':'Next'}</button></div>`;
  panel.querySelector('.tutorial-skip').onclick=()=>close(true);
  panel.querySelector('[data-tutorial-back]').onclick=()=>{index--;render()};
  panel.querySelector('[data-tutorial-next]').onclick=()=>{if(index===tutorial.steps.length-1)close(true);else{index++;render()}};
  panel.querySelector('[data-tutorial-sketch]')?.addEventListener('click',()=>{sketchDetailed=!sketchDetailed;render();panel.querySelector('[data-tutorial-sketch]').focus({preventScroll:true})});
  panel.querySelectorAll('[data-tutorial-answer]').forEach(button=>button.onclick=()=>{
   const selected=Number(button.dataset.tutorialAnswer);answers.set(index,selected);
   panel.querySelectorAll('[data-tutorial-answer]').forEach(b=>{b.setAttribute('aria-pressed',String(b===button));b.classList.toggle('is-correct',b===button&&selected===step.question.correct)});
   feedback((selected===step.question.correct?'That’s right. ':'Try thinking about it this way: ')+step.question.feedback);
  });
  if(answers.has(index))feedback(step.question.feedback);
  if(tried.has(index))feedback(step.success);
  panel.scrollTop=0;
  target=root.querySelector(step.target);
  if(target){
   for(let ancestor=target.parentElement;ancestor&&ancestor!==root;ancestor=ancestor.parentElement)if(ancestor.tagName==='DETAILS')ancestor.open=true;
   target.classList.add('tutorial-target');
   target.scrollIntoView({behavior:'instant',block:'start',inline:'nearest'});
  }
  spacer.style.height=(panel.getBoundingClientRect().height+110)+'px';
  if(step.watch){
   stepAbort=new AbortController();
   root.addEventListener(step.event,event=>{
    if(!(event.target instanceof Element)||!event.target.closest(step.watch))return;
    if(step.event==='input'&&event.target.type!=='range'&&!event.target.value?.trim())return;
    tried.add(index);feedback(step.success);
    const result=step.reveal&&root.querySelector(step.reveal);
    if(result){target?.classList.remove('tutorial-target');target=result;target.classList.add('tutorial-target');target.scrollIntoView({behavior:'instant',block:'start',inline:'nearest'})}
   },{signal:stepAbort.signal});
  }
  panel.querySelector('h2').focus({preventScroll:true});
 }
 function start(){
  if(disposed||!root.isConnected)return;
  close();index=0;answers.clear();tried.clear();sketchDetailed=false;
  spacer=document.createElement('div');spacer.className='tutorial-space';spacer.setAttribute('aria-hidden','true');root.append(spacer);
  panel=document.createElement('aside');panel.className='tutorial-panel';panel.setAttribute('role','region');panel.setAttribute('aria-labelledby','interactive-tutorial-title');document.body.append(panel);
  document.body.classList.add('tutorial-active');document.addEventListener('keydown',onKey,true);
  replay.setAttribute('aria-expanded','true');render();
 }
 replay.setAttribute('aria-expanded','false');replay.onclick=start;
 startFrame=requestAnimationFrame(start);
 return()=>{disposed=true;cancelAnimationFrame(startFrame);close();launcher.remove()};
}