Dance Tonite in WebVR

Ero entusiasta quando il team di Google Data Arts ha contattato Moniker e me per lavorare insieme per esplorare le possibilità offerte da WebVR. Ho seguito il lavoro del suo team nel corso degli anni e i suoi progetti mi hanno sempre colpito. La nostra collaborazione ha dato vita a Dance Tonite, un'esperienza di ballo in VR in continua evoluzione con gli LCD Soundsystem e i loro fan. Ecco come abbiamo fatto.

Il concetto

Abbiamo iniziato con lo sviluppo di una serie di prototipi utilizzando WebVR, uno standard aperto che consente di accedere alla VR visitando un sito web con il browser. L'obiettivo è semplificare l'accesso alle esperienze VR per tutti, indipendentemente dal dispositivo in uso.

Abbiamo preso molto sul serio il problema. Qualsiasi cosa abbiamo ideato dovrebbe funzionare su tutti i tipi di VR, dai visori VR che funzionano con gli smartphone, come Daydream View di Google, Cardboard e Gear VR di Samsung, ai sistemi room-scale come HTC VIVE e Oculus Rift, che riflettono i tuoi movimenti fisici nell'ambiente virtuale. Forse la cosa più importante è che ci è sembrato nello spirito del web creare qualcosa che funzioni anche per chi non possiede un dispositivo VR.

1. Motion capture fai-da-te

Poiché volevamo coinvolgere gli utenti in modo creativo, abbiamo iniziato a esaminare le possibili modalità di partecipazione ed espressione di sé tramite la realtà virtuale. Siamo rimasti colpiti dalla precisione dei movimenti e guardarsi intorno in VR e dalla fedeltà del pubblico. Questo ci ha dato un'idea. Invece di chiedere agli utenti di guardare o creare qualcosa, che ne dici di registrare i loro movimenti?

Qualcuno che si registra su Dance Tonite. Lo schermo alle sue spalle
  mostra ciò che vede nell'auricolare

Abbiamo realizzato un prototipo in cui abbiamo registrato le posizioni dei nostri occhiali e dei controller per la realtà virtuale mentre ballavamo. Abbiamo sostituito le posizioni registrate con forme astratti e siamo rimasti sbalorditi dai risultati. I risultati erano molto umani e avevano molto carattere. Abbiamo capito subito che potevamo utilizzare WebVR per fare il motion capture a casa a basso costo.

Con WebVR, lo sviluppatore ha accesso alla posizione e all'orientamento della testa dell'utente tramite l'oggetto VRPose. Questo valore viene aggiornato a ogni frame dall'hardware VR in modo che il codice possa visualizzare nuovi frame dal punto di vista corretto. Tramite l'API GamePad con WebVR, possiamo anche accedere alla posizione/all'orientamento dei controller degli utenti tramite l'oggetto GamepadPose. Memorizziamo tutti questi valori di posizione e orientamento per ogni fotogramma, creando una "registrazione" dei movimenti dell'utente.

2. Minimalismo e costumi

Con le odierne apparecchiature VR con scala stanza, possiamo rilevare tre punti del corpo dell'utente: la testa e due mani. In Dance Tonite, volevamo mantenere l'attenzione sull'umanità nel movimento di questi tre punti nello spazio. Per ottenere questo risultato, abbiamo cercato di ridurre al minimo l'estetica per concentrarci sul movimento. Ci è piaciuta l'idea di mettere al lavoro le menti delle persone.

Questo video che mostra il lavoro dello psicologo svedese Gunnar Johansson è stato uno degli esempi a cui abbiamo fatto riferimento quando abbiamo deciso di semplificare al massimo le cose. Mostra come i puntini bianchi fluttuanti siano immediatamente riconoscibili come corpi in movimento.

Per quanto riguarda la parte visiva, ci siamo ispirati alle stanze colorate e ai costumi geometrici di questa registrazione del 1970 della messinscena di Margarete Hastings del Triadic Ballet di Oskar Schlemmer.

Mentre Schlemmer scelse i costumi geometrici astratti per limitare i movimenti dei ballerini a quelli di pupazzi e marionette, il nostro obiettivo per Dance Tonite era opposto.

Alla fine abbiamo basato la nostra scelta delle forme sulla quantità di informazioni trasmesse dalla rotazione. Un globo ha lo stesso aspetto, indipendentemente da come viene ruotato, ma un cono punta nella direzione in cui appare e ha un aspetto diverso da quello anteriore e posteriore.

3. Pedale ad anello per il movimento

Volevamo mostrare grandi gruppi di persone registrate che ballano e si muovono tra loro. Questa operazione dal vivo non sarebbe fattibile, dato che il numero di dispositivi VR non è sufficiente. Tuttavia, volevamo comunque avere gruppi di persone che reagivano tra loro attraverso il movimento. Ci ha ricordato la performance ricorsiva di Norman McClaren nel suo video del 1964 "Canon".

L'esibizione di McClaren presenta una serie di movimenti altamente coreografati che iniziano a interagire tra loro dopo ogni loop. Proprio come un pedale loop in musica, in cui i musicisti si esibiscono sovrapponendo diversi pezzi di musica dal vivo, volevamo vedere se fosse possibile creare un ambiente in cui gli utenti potessero improvvisare liberamente versioni più perse delle esibizioni.

4. Stanze interconnesse

Stanze interconnesse

Come per molti brani, le tracce degli LCD Soundsystem sono costruite utilizzando misure con tempi precisi. La loro traccia, Tonite, è presente nel nostro progetto e presenta misurazioni che durano esattamente 8 secondi. Volevamo che gli utenti generassero una performance per ogni loop di 8 secondi. Anche se il ritmo di queste misure non cambia, i contenuti musicali sì. Man mano che il brano progredisce, ci sono momenti con parti vocali e strumenti diversi a cui gli artisti possono reagire in modi diversi. Ognuna di queste misure è espressa come una stanza in cui le persone possono realizzare un'esibizione adatta.

Ottimizzazioni per le prestazioni: non perdere frame

Creare un'esperienza VR multipiattaforma che funzioni su un'unica base di codice con prestazioni ottimali per ogni dispositivo o piattaforma non è un'impresa semplice.

In VR, una delle cose più nauseanti che puoi provare è che la frequenza fotogrammi non tiene il passo con i tuoi movimenti. Se ruoti la testa, ma gli elementi visivi che vedono gli occhi non corrispondono al movimento percepito dall'orecchio interno, si verifica un'immediata abbandono dello stomaco. Per questo motivo, dovevamo evitare ritardi eccessivi della frequenza frame. Ecco alcune ottimizzazioni che abbiamo implementato.

1. Geometria del buffer con istanze

Poiché l'intero progetto utilizza solo una manciata di oggetti 3D, abbiamo potuto ottenere un enorme miglioramento delle prestazioni utilizzando la geometria buffer con istanze. In sostanza, consente di caricare un oggetto sulla GPU una volta e di disegnare tutte le "istanze" di quell'oggetto che vuoi in una singola chiamata di disegno. In Dance Tonite abbiamo solo 3 oggetti diversi (un cono, un cilindro e una stanza con un buco), ma potenzialmente centinaia di copie di questi oggetti. La geometria buffer delle istanze fa parte di ThreeJS, ma abbiamo utilizzato il fork sperimentale e in corso di Dusan Bosnjak che implementa THREE.InstanceMesh, il che semplifica molto il lavoro con la geometria buffer delle istanze.

2. Evitare il garbage collector

Come molti altri linguaggi di scripting, JavaScript libera automaticamente la memoria individuando gli oggetti allocati che non vengono più utilizzati. Questo processo è chiamato garbage collection.

Gli sviluppatori non hanno alcun controllo su quando ciò accade. Il netturbino potrebbe comparire alle nostre porte in qualsiasi momento e iniziare a svuotare i bidoni, con conseguente perdita di frame mentre fa il suo lavoro.

La soluzione è produrre meno rifiuti possibile riciclando gli oggetti. Invece di creare un nuovo oggetto vettore per ogni calcolo, abbiamo contrassegnato gli oggetti scratch per il riutilizzo. Poiché li conserviamo spostando il riferimento a questi elementi al di fuori del nostro ambito, non sono stati contrassegnati per la rimozione.

Ad esempio, ecco il nostro codice per convertire la matrice di località della testa e delle mani dell'utente nell'array di valori di posizione/rotazione che memorizziamo per ogni frame. Riutilizzando SERIALIZE_POSITION, SERIALIZE_ROTATION e SERIALIZE_SCALE, evitiamo l'allocazione della memoria e la raccolta dei rifiuti che avverrebbe se creassimo nuovi oggetti ogni volta che viene chiamata la funzione.

const SERIALIZE_POSITION = new THREE.Vector3();
const SERIALIZE_ROTATION = new THREE.Quaternion();
const SERIALIZE_SCALE = new THREE.Vector3();
export const serializeMatrix = (matrix) => {
    matrix.decompose(SERIALIZE_POSITION, SERIALIZE_ROTATION, SERIALIZE_SCALE);
    return SERIALIZE_POSITION.toArray()
    .concat(SERIALIZE_ROTATION.toArray())
    .map(compressNumber);
};

3. Serializzazione del movimento e della riproduzione progressiva

Per acquisire i movimenti degli utenti in VR, dovevamo serializzare la posizione e la rotazione dell'headset e dei controller e caricare questi dati sui nostri server. Abbiamo iniziato ad acquisire le matrici di trasformazione complete di ogni frame. Il rendimento è stato buono, ma con 16 numeri moltiplicati per 3 posizioni ciascuno a 90 fotogrammi al secondo, si sono generati file di dimensioni molto grandi e quindi lunghe attese durante il caricamento e il download dei dati. Estrai solo i dati posizionali e di rotazione dalle matrici di trasformazione siamo riusciti a ridurre questi valori da 16 a 7.

Poiché i visitatori del web spesso fanno clic su un link senza sapere esattamente cosa aspettarsi, abbiamo bisogno di mostrare rapidamente i contenuti visivi, altrimenti i visitatori abbandoneranno la pagina in pochi secondi.

Per questo motivo, volevamo assicurarci che il nostro progetto potesse iniziare a essere riprodotto il prima possibile. Inizialmente, abbiamo usato JSON come formato per caricare i dati sui movimenti. Il problema è che dobbiamo caricare il file JSON completo prima di poterlo analizzare. Non molto progressivo.

Per fare in modo che un progetto come Dance Tonite venga visualizzato alla massima frequenza fotogrammi possibile, il browser ha a disposizione solo una piccola quantità di tempo per ogni frame per i calcoli JavaScript. Se impieghi troppo tempo, le animazioni iniziano a essere discontinue. All'inizio, abbiamo riscontrato interruzioni durante la decodifica di questi enormi file JSON dal browser.

Ci siamo imbattuti in un pratico formato di flussi di dati basato su NDJSON o JSON delimitato da nuova riga. Il trucco è creare un file con una serie di stringhe JSON valide, ciascuna su una riga. In questo modo puoi analizzare il file durante il caricamento, il che ci consente di visualizzare le prestazioni prima che vengano caricate completamente.

Ecco come appare una sezione di una delle nostre registrazioni:

{"fps":15,"count":1,"loopIndex":"1","hideHead":false}
[-464,17111,-6568,-235,-315,-44,9992,-3509,7823,-7074, ... ]
[-583,17146,-6574,-215,-361,-38,9991,-3743,7821,-7092, ... ]
[-693,17158,-6580,-117,-341,64,9993,-3977,7874,-7171, ... ]
[-772,17134,-6591,-93,-273,205,9994,-4125,7889,-7319, ... ]
[-814,17135,-6620,-123,-248,408,9988,-4196,7882,-7376, ... ]
[-840,17125,-6644,-173,-227,530,9982,-4174,7815,-7356, ... ]
[-868,17120,-6670,-148,-183,564,9981,-4069,7732,-7366, ... ]
...

L'utilizzo di NDJSON ci consente di mantenere la rappresentazione dei dati dei singoli frame delle rappresentazioni come stringhe. Potremmo attendere di aver raggiunto il tempo necessario prima di decodificarli in dati sulla posizione, suddividendo così l'elaborazione necessaria nel tempo.

4. Movimento di interpolazione

Dato che speravamo di mostrare da 30 a 60 spettacoli contemporaneamente, dovevamo ridurre ulteriormente la nostra larghezza di banda. Il team Data Arts ha affrontato lo stesso problema nel progetto Virtual Art Sessions, in cui vengono riprodotte registrazioni di artisti che dipingono in VR utilizzando Tilt Brush. L'azienda ha risolto il problema creando versioni intermedie dei dati utente con frequenze fotogrammi più basse e interpolando i frame durante la riproduzione. Ci ha sorpreso scoprire che difficilmente riuscivamo a distinguere la differenza tra una registrazione interpolata a 15 FPS e la registrazione originale a 90 FPS.

Per verificare, puoi forzare Dance Tonite a riprodurre i dati a varie velocità utilizzando la stringa di query ?dataRate=. Puoi utilizzarlo per confrontare il movimento registrato a 90 frame al secondo, 45 frame al secondo o 15 frame al secondo.

Per la posizione, eseguiamo un'interpolazione lineare tra il fotogramma chiave precedente e quello successivo, in base alla distanza temporale tra i fotogrammi chiave (rapporto):

const { x: x1, y: y1, z: z1 } = getPosition(previous, performanceIndex, limbIndex);
const { x: x2, y: y2, z: z2 } = getPosition(next, performanceIndex, limbIndex);
interpolatedPosition = new THREE.Vector3();
interpolatedPosition.set(
    x1 + (x2 - x1) * ratio,
    y1 + (y2 - y1) * ratio,
    z1 + (z2 - z1) * ratio
    );

Per l'orientamento, viene eseguita un'interpolazione lineare sferica (slerp) tra i fotogrammi chiave. L'orientamento viene archiviato come quaternioni.

const quaternion = getQuaternion(previous, performanceIndex, limbIndex);
quaternion.slerp(
    getQuaternion(next, performanceIndex, limbIndex),
    ratio
    );

5. Sincronizzare i movimenti con la musica

Per sapere quale frame delle animazioni registrate riprodurre, dobbiamo conoscere il tempo corrente della musica fino al millisecondo. È emerso che, anche se l'elemento HTML Audio è perfetto per caricare e riprodurre progressivamente l'audio, la proprietà time che fornisce non cambia in sincronia con il loop del frame del browser. È sempre un po' errata. A volte una frazione di ms troppo presto, altre volte una frazione troppo tardi.

Ciò comporta scatti nelle nostre bellissime registrazioni di ballo, che vogliamo evitare a tutti i costi. Per risolvere il problema, abbiamo implementato il nostro timer proprietario in JavaScript. In questo modo possiamo essere certi che il tempo che passa tra un fotogramma e l'altro sia esattamente uguale al tempo trascorso dall'ultimo fotogramma. Ogni volta che il timer non è sincronizzato con la musica per più di 10 ms, lo sincronizziamo di nuovo.

6. Selezione e nebbia

Ogni storia ha bisogno di un buon finale e volevamo fare qualcosa di sorprendente per gli utenti che sono arrivati alla fine dell'esperienza. Quando esci dall'ultima stanza, entri in un tranquillo paesaggio fatto di coni e cilindri. Ti chiedi: "È la fine?". Mano a mano che ti avvicini al campo, all'improvviso i toni della musica fanno formare diversi gruppi di coni e cilindri in ballerini. Ti ritrovi nel bel mezzo di una grande festa. Quando la musica si interrompe bruscamente, tutto cade a terra.

Per quanto fosse un'esperienza positiva per gli spettatori, ha introdotto alcuni ostacoli al rendimento da risolvere. I dispositivi VR room scale e le relative piattaforme di gioco di fascia alta hanno funzionato perfettamente con le oltre 40 esibizioni extra necessarie per la nostra nuova fine. Tuttavia, le frequenze frame su alcuni dispositivi mobili sono state dimezzate.

Per contrastare questo fenomeno, abbiamo introdotto la nebbia. Dopo una certa distanza, tutto diventa lentamente nero. Poiché non è necessario calcolare o disegnare ciò che non è visibile, eliminiamo le prestazioni nelle stanze non visibili, il che ci consente di risparmiare lavoro sia per la CPU che per la GPU. Ma come decidere la distanza giusta?

Alcuni dispositivi possono gestire qualsiasi cosa tu li metta a fare, mentre altri sono più limitati. Abbiamo scelto di implementare una scala mobile. Misurando continuamente la quantità di frame al secondo, possiamo regolare la distanza della nebbia di conseguenza. Se la frequenza dei fotogrammi funziona senza problemi, proviamo ad aumentare il lavoro di rendering eliminando la nebbia. Se la frequenza frame non è abbastanza fluida, avviciniamo la nebbia per saltare le prestazioni di rendering nell'oscurità.

// this is called every frame
// the FPS calculation is based on stats.js by @mrdoob
tick: (interval = 3000) => {
    frames++;
    const time = (performance || Date).now();
    if (prevTime == null) prevTime = time;
    if (time > prevTime + interval) {
    fps = Math.round((frames * 1000) / (time - prevTime));
    frames = 0;
    prevTime = time;
    const lastCullDistance = settings.cullDistance;

    // if the fps is lower than 52 reduce the cull distance
    if (fps <= 52) {
        settings.cullDistance = Math.max(
        settings.minCullDistance,
        settings.cullDistance - settings.roomDepth
        );
    }
    // if the FPS is higher than 56, increase the cull distance
    else if (fps > 56) {
        settings.cullDistance = Math.min(
        settings.maxCullDistance,
        settings.cullDistance + settings.roomDepth
        );
    }
    }

    // gradually increase the cull distance to the new setting
    cullDistance = cullDistance * 0.95 + settings.cullDistance * 0.05;

    // mask the edge of the cull distance with fog
    viewer.fog.near = cullDistance - settings.roomDepth;
    viewer.fog.far = cullDistance;
}

Qualcosa per tutti: creare esperienze VR per il web

Progettare e sviluppare esperienze asimmetriche multipiattaforma significa tenere conto delle esigenze di ogni utente in base al suo dispositivo. Per ogni decisione di progettazione, dovevamo capire in che modo ciò potrebbe influire sugli altri. Come puoi assicurarti che i contenuti che vedi in VR siano altrettanto eccitanti rispetto a quelli che vedi in VR e viceversa?

1. La sfera gialla

Quindi, i nostri utenti di VR a stanza completa realizzeranno le esibizioni, ma come faranno gli utenti di dispositivi VR mobile (come Cardboard, Daydream View o Samsung Gear) a vivere il progetto? Per questo, abbiamo introdotto un nuovo elemento nel nostro ambiente: la sfera gialla.

La sfera gialla
La sfera gialla

Quando guardi il progetto in VR, lo fai dal punto di vista della sfera gialla. Mentre ti sposti da una stanza all'altra, i ballerini reagiscono alla tua presenza. Ti fanno dei gesti, ballano intorno a te, fanno dei movimenti buffi dietro di te e si spostano rapidamente per non urtare contro di te. La sfera gialla è sempre al centro dell'attenzione.

Il motivo è che, durante la registrazione di un'esibizione, la sfera gialla si muove nel centro della stanza in sincronia con la musica e torna indietro. La posizione della sfera dà all'artista un'idea di dove si trova nel tempo e di quanto tempo gli rimane nel loop. Offre un punto di riferimento naturale su cui basare il rendimento.

2. Un altro punto di vista

Non volevamo escludere gli utenti che non utilizzano la realtà virtuale, soprattutto perché probabilmente costituirebbero il nostro pubblico più numeroso. Invece di creare un'esperienza VR simulata, volevamo offrire ai dispositivi con schermo un'esperienza unica. Abbiamo avuto l'idea di mostrare le prestazioni dall'alto, da una prospettiva isometrica. Questa prospettiva ha una lunga storia nei giochi per computer. È stato utilizzato per la prima volta in Zaxxon, un gioco di sparatutto spaziale del 1982. Mentre gli utenti della VR sono molto impegnativi, la prospettiva isometrica offre una visione divina dell'azione. Abbiamo scelto di aumentare leggermente le dimensioni dei modelli, dando un tocco di estetica da casa delle bambole.

3. Ombre: fai finta finché non ce la fai

Abbiamo riscontrato che alcuni utenti avevano difficoltà a percepire la profondità nel nostro punto di vista isometrico. Sono abbastanza sicuro che sia per questo motivo che Zaxxon è stato anche uno dei primi giochi per computer della storia a proiettare un'ombra dinamica sotto gli oggetti in volo.

Ombre

A quanto pare, creare ombre in 3D è difficile. in particolare per i dispositivi ristretti come i telefoni cellulari. Inizialmente abbiamo dovuto prendere la difficile decisione di eliminarli dall'equazione, ma dopo aver chiesto consiglio all'autore di Three.js e all'esperto hacker demo Mr doob, quest'ultimo ha avuto la brillante idea di… simularli.

Invece di dover calcolare in che modo ciascuno dei nostri oggetti galleggianti oscura le nostre lampade e quindi proietta ombre di forme diverse, disegniamo la stessa immagine di trama circolare sfocata sotto ciascuno di essi. Dal momento che le nostre immagini non cercano di imitare la realtà, abbiamo scoperto che potremmo riuscirci facilmente con poche modifiche. Quando gli oggetti si avvicinano al suolo, le texture diventano più scure e più piccole. Quando si spostano verso l'alto, le texture diventano più trasparenti e grandi.

Per crearli, abbiamo utilizzato questa texture con una sfumatura da bianco a nero (senza trasparenza alpha). Impostiamo il materiale come trasparente e usiamo una combinazione sottrattiva. In questo modo possono armonizzarsi senza problemi quando si sovrappongono:

function createShadow() {
    const texture = new THREE.TextureLoader().load(shadowTextureUrl);
    const material = new THREE.MeshLambertMaterial({
        map: texture,
        transparent: true,
        side: THREE.BackSide,
        depthWrite: false,
        blending: THREE.SubtractiveBlending,
    });
    const geometry = new THREE.PlaneBufferGeometry(0.5, 0.5, 1, 1);
    const plane = new THREE.Mesh(geometry, material);
    return plane;
    }

4. Essere presente

Facendo clic sulle teste di un artista, i visitatori senza visore VR possono guardare le cose dal punto di vista del ballerino. Da questa angolazione, diventano evidenti molti piccoli dettagli. Cercando di mantenere le esibizioni in passi, i ballerini si guardano rapidamente. Quando la sfera entra nella stanza, li vedi guardare nervosamente nella sua direzione. Anche se, in qualità di spettatore, non puoi influenzare questi movimenti, la sensazione di immersione è sorprendentemente efficace. Ancora una volta, abbiamo preferito optare per questa soluzione anziché presentare ai nostri utenti una versione VR simulata controllata con il mouse.

5. Condividere registrazioni

Sappiamo quanto possa essere soddisfacente realizzare una registrazione con una coreografia complessa di 20 livelli di artisti che reagiscono l'uno all'altro. Sapevamo che i nostri utenti probabilmente avrebbero voluto mostrarlo ai loro amici. Ma un fermo immagine di questa impresa non comunica abbastanza. Volevamo invece consentire agli utenti di condividere i video delle loro esibizioni. In realtà, perché non una GIF? Le nostre animazioni sono a ombreggiatura piatta, perfette per le tavolozze dei colori limitate del formato.

Condivisione delle registrazioni

Abbiamo utilizzato GIF.js, una libreria JavaScript che consente di codificare le GIF animate dal browser. Offloada la codifica dei frame su worker web che possono essere eseguiti in background come processi separati, in modo da poter sfruttare più processori che lavorano fianco a fianco.

Purtroppo, con la quantità di frame necessaria per le animazioni, il processo di codifica era ancora troppo lento. Il formato GIF è in grado di creare file di piccole dimensioni utilizzando una tavolozza di colori limitata. Abbiamo riscontrato che la maggior parte del tempo veniva spesa per trovare il colore più simile per ogni pixel. Abbiamo potuto ottimizzare questa procedura decuplicandola inserendo una piccola scorciatoia: se il colore del pixel è uguale all'ultimo, utilizza lo stesso colore della tavolozza di prima.

A questo punto le codifiche erano veloci, ma le dimensioni dei file GIF risultanti erano troppo grandi. Il formato GIF consente di indicare come ogni frame deve essere visualizzato sopra l'ultimo definendone il metodo di eliminazione. Per ottenere file più piccoli, invece di aggiornare ogni pixel per ogni frame, aggiorniamo solo i pixel che sono cambiati. Anche se ha rallentato di nuovo il processo di codifica, abbiamo ridotto notevolmente le dimensioni dei file.

6. Solida base: Google Cloud e Firebase

Il backend di un sito di "contenuti generati dagli utenti" può spesso essere complicato e fragile, ma abbiamo creato un sistema semplice e solido grazie a Google Cloud e Firebase. Quando un artista carica un nuovo ballo nel sistema, viene autenticato in forma anonima da Firebase Authentication. Riceveranno l'autorizzazione a caricare la registrazione in uno spazio temporaneo utilizzando Cloud Storage per Firebase. Al termine del caricamento, la macchina client chiama un trigger HTTP di Cloud Functions for Firebase utilizzando il proprio token Firebase. Viene attivato un processo del server che convalida l'invio, crea un record di database e sposta la registrazione in una directory pubblica su Google Cloud Storage.

Su un terreno solido

Tutti i nostri contenuti pubblici sono archiviati in una serie di file di tipo flat in un bucket Cloud Storage. Ciò significa che i nostri dati sono rapidamente accessibili in tutto il mondo e non dobbiamo preoccuparci che carichi di traffico elevati influiscano in alcun modo sulla disponibilità dei dati.

Abbiamo utilizzato un database Firebase Realtime e gli endpoint di Cloud Function per creare un semplice strumento di moderazione/cura che ci consente di guardare ogni nuovo video inviato in VR e pubblicare nuove playlist da qualsiasi dispositivo.

7. Service worker

I service worker sono un'innovazione abbastanza recente che aiuta a gestire la memorizzazione nella cache degli asset del sito web. Nel nostro caso, i worker dei servizi caricano i nostri contenuti a velocità incredibile per i visitatori di ritorno e consentono persino al sito di funzionare offline. Si tratta di funzionalità importanti, in quanto molti dei nostri visitatori utilizzano connessioni mobili di diversa qualità.

L'aggiunta di un servizio worker al progetto è stata facile grazie a un utile plug-in webpack che gestisce la maggior parte del lavoro pesante. Nella configurazione riportata di seguito, generiamo un service worker che memorizza automaticamente nella cache tutti i nostri file statici. Recuperi l'ultimo file della playlist dalla rete, se disponibile, poiché la playlist verrà aggiornata continuamente. Tutti i file JSON di registrazione dovrebbero essere estratti dalla cache, se disponibili, perché non cambieranno mai.

const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin');
config.plugins.push(
    new SWPrecacheWebpackPlugin({
    dontCacheBustUrlsMatching: /\.\w{8}\./,
    filename: 'service-worker.js',
    minify: true,
    navigateFallback: 'index.html',
    staticFileGlobsIgnorePatterns: [/\.map$/, /asset-manifest\.json$/],
    runtimeCaching: [{
        urlPattern: /playlist\.json$/,
        handler: 'networkFirst',
    }, {
        urlPattern: /\/recordings\//,
        handler: 'cacheFirst',
        options: {
        cache: {
            maxEntries: 120,
            name: 'recordings',
        },
        },
    }],
    })
);

Al momento, il plug-in non gestisce gli asset multimediali caricati progressivamente, come i nostri file musicali, quindi abbiamo risolto il problema impostando l'intestazione Cache-Control di Cloud Storage su questi file su public, max-age=31536000 in modo che il browser memorizzi nella cache il file per un massimo di un anno.

Conclusione

Non vediamo l'ora di scoprire in che modo gli artisti contribuiranno a questa esperienza e la utilizzeranno come strumento per l'espressione creativa tramite il movimento. Abbiamo rilasciato tutto il codice open source, che puoi trovare all'indirizzo https://github.com/puckey/dance-tonite. In questa fase iniziale della realtà virtuale e in particolare del WebVR, non vediamo l'ora di scoprire le nuove direzioni creative e inaspettate che prenderà questo nuovo mezzo. Continua a ballare.