accessibility first pass

This commit is contained in:
Utopiah
2026-07-03 16:58:37 +02:00
parent f483302733
commit 0a065cf846
5 changed files with 87 additions and 36 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
node_modules/
package-lock.json
server/adminToken

View File

@@ -70,6 +70,9 @@ function addIssues(){
a.classList.add('issue')
a.classList.add('issueVisible')
el.appendChild(a)
let s = document.createElement('span')
s.innerText = ', '
el.appendChild(s)
})
})
}
@@ -92,6 +95,7 @@ function voteOnContent(el){
}
let voteEl = document.createElement("select")
voteEl.title = 'voting on ' + el.id
voteEl.addEventListener("change", (event) => {
updateScore(voteEl)
el.addEventListener("click", checkboxClick);
@@ -147,13 +151,14 @@ let remixesMetaData = []
function addRemix( rmx, parentEl ) {
let el = document.createElement('div')
voteOnContent(el)
el.id = "remix_" + rmx.replace(/.json$/,'')
let timestamp = Number(rmx.replace(/.json$/,'').replace(/.*[a-zA-Z]/,'') ) // not all have one, e.g. translation example
el.timestamp = timestamp // can be use to sort a posteriori
let date = ' unknown'
voteOnContent(el)
if (timestamp > 0) date = new Date( timestamp )
el.title = rmx + '\n(remix date ' + date + ')'
// not descriptive enough, also needs more meta-data knowing though that text input in XR is difficult XR wise
// use screenshot as background, if available
el.style.backgroundImage = "url('/screenshots/1774109944433.png')"
// could try to get from the remix file itself
@@ -241,6 +246,8 @@ function addGamePreviews(){
// use screenshot as background, if available
el.style.backgroundImage = "url('"+prefix+g+extension+"')"
el.classList.add('remix')
// el.title // needs more meta-data knowing though that text input in XR is difficult XR wise
// could be delegated to 2D browsing after, in debrief session
//el.classList.add('triangle-topleft')
let a = document.createElement('a')
a.href = "games.html?&play=" + g

View File

@@ -69,6 +69,9 @@ AFRAME.registerComponent('onboarding', {
});
AFRAME.scenes[0].addEventListener("executing", event => {
// richer feedback for accessibility
// cancellation could be considered here but would need a specific gesture and a stack of calls which can not all be reverted
// event.detail.element.setAttribute("sound", "src:url(content/notes/t2.mp3); volume: 0.05; loop: false;")
// event.detail.element.components.sound.playSound()
// event.detail.element.object3D.translateZ(.2) // has been rotated

View File

@@ -5,6 +5,9 @@
<script src=remixes.js></script>
<script src=content.js></script>
</head>
<title>
Federating pedagogical immersive experiences / Biggu's Gate onboarding
</title>
<style>
/* from https://projects.verou.me/css3patterns/#lined-paper */
body {
@@ -18,22 +21,32 @@ body {
}
</style>
<div style='position:fixed;z-index:1; top: 0%; left: 0%; border-bottom: 70px solid transparent; border-left: 70px solid #eee; '>
<a href="https://git.benetou.fr/utopiah/biggu_s_gate/issues/">
<img style='position:fixed;left:10px;' title='code repository' src='https://git.benetou.fr/assets/img/logo.svg'>
</a>
</div>
<div style='position:fixed;z-index:1; top: 0%; left: 0%'>
<a href="https://git.benetou.fr/utopiah/biggu_s_gate/issues/">
<img title='code repository' alt='code repository' src='https://git.benetou.fr/assets/img/logo.svg' />
</a>
</div>
<div>
<center>Welcome <span id=nicknamespan></span> to Federating pedagogical immersive experiences / Biggu's Gate</center>
<br>
<br>
To properly experience this content you <b>do</b> need a VR headset 🕶️ with hand tracking 🙌capabilities, e.g. Meta Quest 1, 2, 3, Pro, or Apple Vision Pro, or Lynx XR1 or PICO. If you do not have one then please consider instead joining an upcoming <a href=https://git.benetou.fr/utopiah/biggu_s_gate/wiki/Workshops>workshop</a>, mostly in Brussels at the moment. If you do want to have your own headset please read <a href=https://git.benetou.fr/utopiah/biggu_s_gate/wiki/Selecting-a-headset>headset suggestions</a>.
<span id='explanation'>To properly experience this content you <b>do</b> need a VR headset 🕶️ with hand tracking 🙌capabilities, e.g. Meta Quest 1, 2, 3, Pro, or Apple Vision Pro, or Lynx XR1 or PICO.
<br>
<ol>
<li>Try remixing your <a href=remixed_room.html?room=onboarding>first experience</a> and change the color of the sky to <font color=pink>pink</font>.</li>
<br>
<span id='notxrcompatible'>Unfortunately you do <b>not</b> have one. Please consider instead joining an upcoming <a href=https://git.benetou.fr/utopiah/biggu_s_gate/wiki/Workshops>workshop</a>, mostly in Brussels at the moment. If you do want to have your own headset please read <a href=https://git.benetou.fr/utopiah/biggu_s_gate/wiki/Selecting-a-headset>headset suggestions</a>.</span>
<span style="display:none" id='xrcompatible'>It seems you <b>do</b> have one! Please have fun and consider helping others discover it with your headset too!</span>
</span>
<br>
<br>
<button id=forcexr onclick="explanationsinxr.style.display = 'block'">Show content anyway despite not having an XR headset</button>
<br>
<ol style="display:none" id=explanationsinxr>
<li>Try remixing your <a href=remixed_room.html?room=onboarding>first experience in VR</a> and change the color of the sky to <font color=pink>pink</font>.</li>
<li>...Once you managed try remixing another experience, or even the one you just did, <a href=remixed_room.html?forcedinstructionset=1>with the extended set of commands</a> and change the scale of something instead.</li>
<li>... If you also managed that, you can <a href=remixed_room.html?loadRemix=remix__inspectoreditorstart.1774335924.json>edit the 3D scene, e.g. moving elements around, using the editor</a>
<li>... or <a href=editor.html?content=./remixes/remix__instructions_translation_example.json>the translation of instructions to French</a> but you'll need a mobile or a keyboard for that small one</li>
@@ -44,19 +57,6 @@ body {
</ol>
</div>
<br>
<div id=webxrcapableexplanation style="width:50%; margin-left:25%; border:1px;">
If you have a VR headset (could fingerprint a bit, e.g. Wolvic / Quest Browser UA or, better, WebXR capabilities, with risk on Android phone), open this page directly on it.
<br>
<br>
If you are not sure how to open this page on your own headset, you can :
<ul>
<li> open a browser there and type that URL again (you can see <a href=https://copyparty.benetou.fr/Prototypes/ada_biggu_setup_steps/readme.md?v>details</a>)
<li> if this computer or phone is on the same network as your headset, click on this link and open hmd.link on the device
</ul>
(if this text comes with a <font color=red>red</font> background, your browser is not WebXR capable, in <font color=green>green</font> it is)
</div>
<h2>Games</h2>
@@ -67,23 +67,56 @@ body {
<h2>Remixes</h2>
<div id=existingremixes></div>
<button onclick=toggleShowIssueLinks()>show issue links</button>
<button onclick=addIssues()>show issue links</button>
<button onclick=toggleShowIssueLinks()>show issues contextually</button>
<script>
Array.from( explanationsinxr.querySelectorAll("a") ).map( el => {
el.addEventListener("mouseover", (event) => {
if (el.title){
console.log(el.title) // used for hinting in XR, not a traditional way though but could be interesting to explore.
// focus remains in XR browsing though, immersed, so this remains a temporary fallback.
}
})
})
nicknamespan.innerText = nickname
// removed link as confusing for a first time
/*
let a = document.createElement('a')
a.href = "./users/index.html"
a.innerText = nickname
nicknamespan.appendChild(a)
a.title = 'nickname generated for the player, link to profile'
*/
// consider UA testing on Wolvic and OculusBrowser
const compatibleBrowsers = ['Wolvic', 'OculusBrowser']
let compat = compatibleBrowsers.filter( b => navigator.userAgent.includes(b) ).length
// compat > 0 ? showXR() : hideXR()
function showXR(){
console.log('well known WebXR compatible browser detected, required but not sufficient')
notxrcompatible.style.display = 'none'
forcexr.style.display = 'none'
xrcompatible.style.display = 'block'
explanationsinxr.style.display = 'block'
}
function hideXR(){
console.log('no well known WebXR compatible browser detected, might be problematic but checking capabilities instead')
xrcompatible.style.display = 'none'
}
// on capability testing https://developer.mozilla.org/en-US/docs/Web/API/WebXR_Device_API/Startup_and_shutdown#falling_back_to_the_webxr_polyfill
if (navigator.xr) {
webxrcapableexplanation.classList.add('webxrcapable')
if (navigator.xr !== undefined && navigator.xr.isSessionSupported){
const errorHandler = function (err) { hideXR(); console.error("WebXR session support error: " + err.message); };
navigator.xr.isSessionSupported("immersive-vr").then(function (supported) {
// as explained in https://git.benetou.fr/utopiah/biggu_s_gate/issues/37#issuecomment-1858 color change is not sufficient
showXR()
}).catch(errorHandler);
} else {
webxrcapableexplanation.classList.add('webxrincapable')
hideXR()
}
// load up all helpers
// test on username
// if not generate one
// verify against server if unique
</script>
<div>
@@ -93,13 +126,15 @@ body {
<div id=startnewgamefromtempalte></div>
<script>
addIssues()
// load up all helpers
addGamePreviews()
addRemixes()
popularScores() // should be done only after games and remixes are added (event?)
showNetwork()
</script>
<h2>Documentation</h2>
See the repository <a href=https://git.benetou.fr/utopiah/biggu_s_gate/src/branch/main/documentation>documentation</a>.
<li>See the repository <a href=https://git.benetou.fr/utopiah/biggu_s_gate/src/branch/main/documentation>documentation</a></li>
</html>

View File

@@ -344,6 +344,9 @@ function setupRecorder(){
// Success callback
.then((stream) => {
setuprecordstart.removeAttribute('disabled')
setuprecord.disabled = true
mediaRecorder = new MediaRecorder(stream);
mediaRecorder.ondataavailable = (e) => {
chunks.push(e.data);
@@ -409,9 +412,9 @@ function saveAudioFile(){
<button onclick=moveForwardMobile() id=mobile>move forward</button>
<div id=audiofeedbackinterface style="z-index:100; position:fixed; bottom:0">
audio recorder for feedback :
<button onclick="setupRecorder(); setuprecordstart.removeAttribute('disabled'); setuprecord.disabled = true;" id=setuprecord>setup</button>
<button disabled onclick="mediaRecorder.start(); setuprecordstop.removeAttribute('disabled');" id=setuprecordstart>start</button>
<button disabled onclick="mediaRecorder.stop(); playlastrecord.removeAttribute('disabled');" id=setuprecordstop>stop</button>
<button onclick="setupRecorder(); /* interaction moved to setup function */" id=setuprecord>setup</button>
<button disabled onclick="setFeedbackHUD('recording audio'); mediaRecorder.start(); setuprecordstop.removeAttribute('disabled');" id=setuprecordstart>start</button>
<button disabled onclick="setFeedbackHUD('audio recording stopped'); mmediaRecorder.stop(); playlastrecord.removeAttribute('disabled');" id=setuprecordstop>stop</button>
<button disabled onclick="audioElements.at(-1).play(); savelastrecord.removeAttribute('disabled')" id=playlastrecord>play last record</button>
<button disabled onclick="saveAudioFile()" id=savelastrecord>save last record</button>
<input id=audionote />