Tonite de la danse en WebVR

J'ai été ravi que l'équipe Google Data Arts nous a demandé, à Moniker et à moi-même, de travailler ensemble pour explorer les possibilités introduites par WebVR. J'ai regardé le travail de leur équipe au fil des ans et leurs projets m'ont toujours touché. Notre collaboration a donné naissance à Dance Tonite, une expérience de danse en réalité virtuelle en constante évolution avec LCD Soundsystem et ses fans. Voici comment nous avons fait.

Concept

Nous avons commencé par développer une série de prototypes utilisant WebVR, une norme ouverte qui permet de passer en RV en visitant un site Web à l'aide de votre navigateur. L'objectif est de permettre à tous de découvrir plus facilement des expériences de réalité virtuelle, quel que soit l'appareil dont vous disposez.

Nous avons pris cela à cœur. Tout ce que nous avons inventé devrait fonctionner sur tous les types de RV, des casques de RV compatibles avec les téléphones mobiles tels que Google Daydream View, Carboard et Gear VR de Samsung aux systèmes à l'échelle d'une pièce comme HTC VIVE et Oculus Rift, qui reflètent vos mouvements physiques dans votre environnement virtuel. Mais surtout, nous pensons qu'il serait dans l'esprit du Web de créer une application qui fonctionne également pour tous les utilisateurs qui ne possèdent pas d'appareil de RV.

1. Capture de mouvement maison

Comme nous voulions impliquer les utilisateurs de manière créative, nous avons commencé à étudier les possibilités de participer et d'exprimer eux-mêmes grâce à la RV. Nous avons été impressionnés par la précision de vos déplacements et de votre vue en RV, ainsi que par la fidélité qu'il y a à ce mode. Cela nous a donné une idée. Au lieu de demander aux utilisateurs de regarder ou de créer quelque chose, comment enregistrer leurs mouvements ?

Quelqu'un s'enregistre sur Dance Tonite. L'écran derrière lui montre ce qu'il voit dans son casque

Nous avons concocté un prototype dans lequel nous avons enregistré les positions de nos lunettes de réalité virtuelle et de nos contrôleurs pendant que vous dansez. Nous avons remplacé les positions enregistrées par des formes abstraites et nous nous sommes émerveillés des résultats. Les résultats étaient tellement humains et contenaient tellement de personnalité ! Nous avons rapidement réalisé que nous pouvions utiliser WebVR pour effectuer une capture de mouvement chez soi à moindre coût.

Avec WebVR, le développeur a accès à la position et à l'orientation de la tête de l'utilisateur via l'objet VRPose. Cette valeur est mise à jour à chaque image par le matériel de RV afin que votre code puisse afficher de nouvelles images du bon point de vue. Grâce à l'API GamePad avec WebVR, nous pouvons également accéder à la position/orientation des manettes des utilisateurs via l'objet GamepadPose. Nous stockons simplement l'ensemble de ces valeurs de position et d'orientation à chaque image, créant ainsi un "enregistrement" des mouvements de l'utilisateur.

2. Minimalisme et costumes

Avec les équipements de RV actuels à l'échelle d'une salle, nous pouvons suivre trois points du corps de l'utilisateur: sa tête et ses deux mains. Dans Dance Tonite, nous voulions garder l'accent sur l'humanité dans le mouvement de ces trois points dans l'espace. Pour y parvenir, nous avons déployé l'esthétique aussi minimale que possible afin de nous concentrer sur le mouvement. Nous avons aimé l'idée de mettre le cerveau des gens au travail.

Cette vidéo présentant le travail du psychologue suédois Gunnar Johansson est l'un des exemples que nous avons évoqués lorsque nous avons envisagé de supprimer autant que possible certaines choses. Elle montre que les points blancs flottants sont instantanément reconnaissables en tant que corps lorsqu'ils sont vus en mouvement.

Visuellement, nous nous sommes inspirés des pièces colorées et des costumes géométriques dans cet enregistrement de la reconstitution du ballet triadique d'Oskar Schlemmer réalisée par Margarete Hastings en 1970.

Alors que Schlemmer a choisi des costumes géométriques abstraits pour limiter les mouvements de ses danseurs à ceux des marionnettes, nous avions pour objectif inverse de Danse Tonite.

Nous avons fini par baser notre choix de formes sur la quantité d'informations qu'elles transmettaient par rotation. Une orbe a la même apparence, quelle que soit la rotation, mais un cône est réellement orienté dans la direction qu'il regarde et a un aspect différent de l'avant de l'arrière.

3. Bouclez la pédale pour bouger

Nous voulions montrer de grands groupes de personnes enregistrées en train de danser et de bouger. Cela ne serait pas possible, car les appareils de RV ne sont pas suffisamment nombreux. Mais nous voulions toujours que des groupes de personnes réagissent les uns aux autres par le biais du mouvement. Nous nous sommes penchés sur la performance récursive de Norman McClaren dans sa vidéo "Canon" (1964).

McClaren présente une série de mouvements très chorégraphiés qui commencent à interagir les uns avec les autres après chaque boucle. À l'instar d'une pédale en boucle dans le domaine de la musique, où les musiciens jouent avec eux-mêmes en superposant différentes parties de concerts, nous souhaitions voir s'il était possible de créer un environnement dans lequel les utilisateurs pourraient improviser librement des versions plus larges de concerts.

4. Chambres communicantes

Chambres communicantes

Comme beaucoup de musique, les pistes de LCD Soundsystem sont construites à l'aide de mesures minutieuses précises. Son titre, Tonite, qui est présenté dans notre projet, comporte des mesures d'une durée exactement de huit secondes. Nous voulions que les utilisateurs réalisent une performance pour chaque boucle de huit secondes dans le titre. Même si le rythme de ces mesures ne change pas, leur contenu musical change. À mesure que la chanson progresse, les artistes peuvent réagir de différentes manières à des moments avec différents instruments et différentes voix. Chacune de ces mesures est exprimée sous la forme d'une pièce dans laquelle les utilisateurs peuvent réaliser une performance qui lui convient.

Optimisations des performances: ne laissez pas d'images

Créer une expérience de réalité virtuelle multiplate-forme fonctionnant sur un seul codebase et offrant des performances optimales pour chaque appareil ou plate-forme n'est pas une mince affaire.

En RV, l'une des choses les plus agaçantes que vous puissiez ressentir est que la fréquence d'images ne suit pas votre mouvement. Si vous tournez la tête, mais que les éléments visuels que vos yeux voient ne correspondent pas au mouvement de votre oreille interne, cela provoque un épuisement de l'estomac. Pour cette raison, nous devions éviter tout retard important de la fréquence d'images. Voici quelques optimisations que nous avons mises en œuvre.

1. Géométrie du tampon d'instance

Comme l'ensemble de notre projet n'utilise qu'un petit nombre d'objets 3D, nous avons pu améliorer considérablement les performances en utilisant la géométrie de tampon d'instance. Fondamentalement, elle vous permet d'importer une seule fois votre objet dans le GPU et de dessiner autant d'instances de cet objet que vous le souhaitez dans un seul appel de dessin. Dans Dance Tonite, nous n'avons que trois objets différents (un cône, un cylindre et une pièce avec un trou), mais potentiellement des centaines de copies de ces objets. La géométrie de tampon d'instance fait partie de ThreeJS, mais nous avons utilisé la duplication expérimentale et en cours de Dusan Bosnjak qui implémente THREE.InstanceMesh, ce qui facilite grandement l'utilisation de la géométrie de tampon d'instance.

2. Éviter le récupérateur de mémoire

Comme pour de nombreux autres langages de script, JavaScript libère automatiquement de la mémoire en identifiant les objets alloués qui ne sont plus utilisés. Ce processus est appelé récupération de mémoire.

Les développeurs n'ont aucun contrôle sur le moment où cela se produit. Le récupérateur de mémoire peut apparaître à tout moment dans nos portes et commencer à vider la corbeille, ce qui entraîne la perte de certaines images au fur et à mesure qu'elles prennent leur temps.

La solution à ce problème consiste à produire le moins de déchets possible en recyclant les objets. Au lieu de créer un objet vectoriel pour chaque calcul, nous avons marqué les objets nouveaux à réutiliser. Comme nous les avons conservées en excluant notre champ d'application, elles n'ont pas été marquées pour suppression.

Par exemple, voici notre code qui convertit la matrice de localisation de la tête et des mains de l'utilisateur en tableau de valeurs de position/rotation que nous stockons à chaque image. En réutilisant SERIALIZE_POSITION, SERIALIZE_ROTATION et SERIALIZE_SCALE, nous évitons l'allocation de mémoire et la récupération de mémoire qui se produiraient si nous créions de nouveaux objets à chaque appel de la fonction.

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. Sérialisation du mouvement et de la lecture progressive

Afin de capturer les mouvements des utilisateurs en RV, nous devions sérialiser la position et la rotation du casque et des manettes, et importer ces données sur nos serveurs. Nous avons commencé par capturer les matrices de transformation complètes pour chaque frame. Cette méthode a donné de bons résultats, mais avec 16 nombres multipliés par 3 positions à 90 images par seconde, les fichiers étaient très volumineux et les temps d'attente pendant l'importation et le téléchargement des données étaient donc longs. En extrayant uniquement les données de position et rotation des matrices de transformation, nous avons pu faire passer ces valeurs de 16 à 7.

Étant donné que les internautes cliquent souvent sur un lien sans savoir exactement à quoi s'attendre, nous devons afficher le contenu visuel rapidement, sans quoi ils quitteront le site en quelques secondes.

C'est pourquoi nous voulions nous assurer que notre projet pourrait être lancé le plus tôt possible. Au début, nous utilisions le format JSON pour charger nos données de mouvement. Le problème est que nous devons charger le fichier JSON complet avant de pouvoir l'analyser. Pas très progressif.

Pour qu'un projet tel que Danse Tonite s'affiche à la fréquence d'images la plus élevée possible, le navigateur ne dispose que d'une courte durée à chaque frame pour les calculs JavaScript. Si cela prend trop de temps, les animations commencent à stuttering. Au début, nous avons rencontré des saccades, car ces énormes fichiers JSON étaient décodés par le navigateur.

Nous avons trouvé un format pratique de flux de données appelé NDJSON, ou JSON délimité par un retour à la ligne. L'astuce consiste à créer un fichier avec une série de chaînes JSON valides, chacune sur sa propre ligne. Cela vous permet d'analyser le fichier pendant son chargement, ce qui nous permet d'afficher les performances avant leur chargement complet.

Voici à quoi ressemble une section de l'un de nos enregistrements:

{"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'utilisation de NDJSON nous permet de conserver la représentation des données des frames individuels des performances sous forme de chaînes. Nous pourrions attendre d'avoir atteint le temps nécessaire avant de les décoder en données de position, ce qui permet d'étaler le traitement nécessaire au fil du temps.

4. Interpolation du mouvement

Comme nous voulions diffuser entre 30 et 60 performances en même temps, nous devions réduire encore plus notre débit de données. L'équipe Data Arts a abordé le même problème dans son projet Virtual Art Sessions, au cours duquel elle diffuse des enregistrements d'artistes peignant en RV à l'aide de Tilt Brush. Pour résoudre ce problème, l'équipe a créé des versions intermédiaires des données utilisateur avec des fréquences d'images plus faibles et effectue une interpolation entre les images lors de leur lecture. Nous avons été surpris de constater que nous pouvions à peine repérer la différence entre un enregistrement interpolé à 15 FPS et l'enregistrement d'origine à 90 FPS.

Pour le constater par vous-même, vous pouvez forcer Danse Tonite à lire les données à différents rythmes à l'aide de la chaîne de requête ?dataRate=. Vous pouvez l'utiliser pour comparer le mouvement enregistré à 90 images par seconde, 45 images par seconde ou 15 images par seconde.

Pour la position, nous effectuons une interpolation linéaire entre l'image clé précédente et la suivante, en fonction de la proximité dans le temps entre les images clés (ratio):

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
    );

Pour l'orientation, nous effectuons une interpolation linéaire sphérique (slerp) entre les images clés. L'orientation est stockée en tant que quaternions.

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

5. Synchronisation des mouvements avec la musique

Pour savoir quel frame des animations enregistrées est lu, nous devons connaître l'heure actuelle de la musique jusqu'à la milliseconde. Bien que l'élément audio HTML soit parfait pour charger et lire progressivement le son, la propriété de temps qu'il fournit ne change pas au rythme de la boucle de frames du navigateur. C'est toujours un peu décalé. Parfois, une fraction de ms trop tôt, parfois une fraction de seconde trop tard.

Cela entraîne du bégaiement dans nos magnifiques enregistrements de danse, que nous souhaitons éviter à tout prix. Pour y remédier, nous avons implémenté notre propre minuteur en JavaScript. De cette façon, nous pouvons être certains que le temps de passage d'un frame à l'autre correspond exactement à la durée écoulée depuis la dernière image. Chaque fois que le minuteur se désynchronise de plus de 10 ms avec la musique, il est à nouveau synchronisé.

6. Réduction et brouillard

Chaque histoire a besoin d'une bonne fin, et nous voulions faire quelque chose de surprenant pour les utilisateurs qui sont arrivés à la fin de notre expérience. En quittant la dernière pièce, vous entrez dans ce qui ressemble à un paysage calme de cônes et de cylindres. "Est-ce la fin ?" vous vous posez la question. À mesure que vous progressez, les tonalités de la musique créent soudainement différents groupes de cônes et de cylindres pour former des danseurs. Vous vous retrouvez au milieu d'une énorme fête ! Ensuite, alors que la musique s'arrête brusquement, tout tombe au sol.

Bien que cette stratégie ait séduit les spectateurs, elle s'est accompagnée de quelques obstacles sur les performances à surmonter. Les appareils de réalité virtuelle à l'échelle de la salle et leurs équipements de jeu haut de gamme offrent des performances optimales, avec les 40 performances supplémentaires requises pour notre nouvelle extension. Mais la fréquence d'images de certains appareils mobiles a été réduite de moitié.

Pour contrecarrer cela, nous avons introduit du brouillard. Après une certaine distance, tout devient lentement noir. Comme nous n'avons pas besoin de calculer ni de dessiner ce qui n'est pas visible, nous réduisons les performances dans les salles qui ne sont pas visibles, ce qui nous permet d'économiser du travail à la fois pour le processeur et le GPU. Mais comment déterminer la bonne distance ?

Certains appareils peuvent gérer tout ce que vous leur lancez, tandis que d'autres sont plus gênants. Nous avons choisi d'utiliser un barème dégressif. En mesurant en continu le nombre d'images par seconde, nous pouvons ajuster la distance du brouillard en conséquence. Tant que la fréquence d'images fonctionne correctement, nous essayons de poursuivre le rendu en éliminant le brouillard. Si la fréquence d'images n'est pas assez fluide, nous nous rapprochons du brouillard pour éviter les performances de rendu dans l'obscurité.

// 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;
}

Des solutions pour tous: la RV sur le Web

Pour concevoir et développer des expériences asymétriques multiplates-formes, vous devez tenir compte des besoins de chaque utilisateur en fonction de son appareil. Et pour chaque décision de conception, nous devions voir comment cela pouvait avoir un impact sur les autres utilisateurs. Comment faire pour que ce que vous voyez en RV soit tout aussi excitant que sans RV, et inversement ?

1. L'orbe jaune

Nos utilisateurs de RV à l'échelle d'une salle réaliseraient donc des performances, mais comment les utilisateurs d'appareils de RV mobiles (tels que Carnet, Daydream View ou Samsung Gear) vivraient-ils le projet ? Pour cela, nous avons introduit un nouvel élément dans notre environnement: l'orbe jaune.

L&#39;orbe jaune
L&#39;orbe jaune

Lorsque vous regardez le projet en RV, vous le faites du point de vue de l'orbe jaune. Vous flottez d'une pièce à l'autre, et les danseurs réagissent à votre présence. Ils font des gestes vers vous, dansent autour de vous, font des mouvements amusants dans le dos et sortent rapidement de votre chemin pour ne pas vous heurter. La orbe jaune est toujours au centre de l'attention.

En effet, lors de l'enregistrement d'une représentation, l'orbe jaune se déplace au centre de la pièce au rythme de la musique et revient en arrière. La position de l'orbe donne à l'artiste une idée de sa position dans le temps et du temps qu'il reste dans sa boucle. L'objectif est naturellement d'améliorer les performances.

2. Un autre point de vue

Nous ne voulions pas laisser de côté les utilisateurs sans RV, d'autant plus qu'ils constitueraient sans doute notre plus grande audience. Au lieu de créer une fausse expérience de RV, nous voulions offrir une expérience unique aux appareils basés sur un écran. Nous avons eu l'idée de montrer les performances d'en haut d'un point de vue isométrique. Cette perspective a une riche histoire dans les jeux vidéo. Il a été utilisé pour la première fois dans Zaxxon, un jeu de tir spatial de 1982. Alors que les utilisateurs de RV sont au cœur de l'action, la perspective isométrique offre une vision de l'action semblable à celle d'un dieu. Nous avons choisi d'augmenter légèrement les modèles, ce qui donne une touche esthétique à la maison de poupée.

3. Simulez des ombres jusqu 'à ce que vous les réalisiez

Nous avons constaté que certains de nos utilisateurs avaient du mal à voir la profondeur de notre point de vue isométrique. Je suis sûr que c'est pour cette raison que Zaxxon a également été l'un des premiers jeux informatiques de l'histoire à projeter une ombre dynamique sous ses objets volants.

Ombres

Il s'avère qu'il est difficile de créer des ombres en 3D. Surtout pour les appareils encombrés comme les téléphones portables. Au début, nous avons dû prendre la décision difficile de les exclure de l'équation, mais après avoir demandé conseil à l'auteur de Three.js et au hacker de démonstration Mr doob, il a eu l'idée nouvelle de les simuler.

Au lieu de devoir calculer la manière dont chacun de nos objets flottants masque les lumières et crée donc des ombres de différentes formes, nous dessinons la même image de texture floutée circulaire sous chacun d'eux. Comme nos visuels n'essaient pas d'imiter la réalité au départ, nous avons constaté que nous pouvions nous en sortir assez facilement en apportant seulement quelques ajustements. Lorsque les objets se rapprochent du sol, les textures deviennent plus sombres et plus petites. Quand elles sont relevées, nous rendons les textures plus transparentes et plus grandes.

Pour les créer, nous avons utilisé cette texture avec un dégradé du blanc au noir doux (sans transparence alpha). Nous définissons le matériau comme transparent et utilisons le mélange soustractif. Cela les aide à se fondre bien lorsqu'ils se chevauchent:

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. Être présent

En cliquant sur la tête d'un artiste, les visiteurs sans RV peuvent regarder les choses du point de vue du danseur. Sous cet angle, de nombreux petits détails deviennent apparents. Pour continuer à jouer en rythme, les danseurs se regardent rapidement. Alors que les orbes entrent dans la pièce, vous les voyez regarder nerveusement dans sa direction. Si, en tant que spectateur, vous ne pouvez pas influencer ces mouvements, cela traduit étonnamment bien ce sentiment d'immersion. Là encore, nous avons préféré procéder de cette manière plutôt que présenter à nos utilisateurs une fausse version de réalité virtuelle contrôlée par la souris.

5. Partager des enregistrements

Nous savons à quel point vous pouvez être fier lorsque vous réalisez un enregistrement finement chorégraphié de 20 couches d'interprètes réagissant les uns aux autres. Nous savions que nos utilisateurs voudraient probablement la montrer à leurs amis. Mais une image fixe de cet exploit ne communique pas assez. Nous voulions autoriser nos utilisateurs à partager des vidéos de leurs prestations. Pourquoi pas un GIF ? Nos animations sont grisées, idéales pour les palettes de couleurs limitées du format.

Partager des enregistrements

Nous avons adopté GIF.js, une bibliothèque JavaScript qui vous permet d'encoder des GIF animés depuis votre navigateur. Elle décharge l'encodage des frames sur des web workers capables de s'exécuter en arrière-plan en tant que processus distincts, permettant ainsi de tirer parti du fonctionnement de plusieurs processeurs côte à côte.

Malheureusement, avec la quantité de frames dont nous avions besoin pour les animations, le processus d'encodage était encore trop lent. Le GIF est capable de créer de petits fichiers en utilisant une palette de couleurs limitée. Nous avons constaté que la plupart du temps consistait à trouver la couleur la plus proche pour chaque pixel. Nous avons pu optimiser ce processus par 10 en effectuant un petit raccourci: si la couleur du pixel est identique à celle du dernier, utilisez la même couleur de la palette qu'auparavant.

Nous avions maintenant des encodages rapides, mais les fichiers GIF obtenus étaient trop volumineux. Le format GIF vous permet d'indiquer comment chaque image doit s'afficher au-dessus de la dernière en définissant sa méthode de suppression. Pour obtenir des fichiers plus petits, au lieu de mettre à jour chaque pixel pour chaque frame, nous ne mettons à jour que les pixels qui ont été modifiés. En ralentissant à nouveau le processus d'encodage, cela nous a permis de réduire considérablement la taille des fichiers.

6. Base solide: Google Cloud et Firebase

Le backend d'un site à "contenu généré par l'utilisateur" peut être complexe et fragile, mais nous avons mis au point un système simple et robuste grâce à Google Cloud et Firebase. Lorsqu'un artiste importe une nouvelle danse dans le système, il est authentifié de manière anonyme par Firebase Authentication. Ils sont autorisés à importer leur enregistrement dans un espace temporaire à l'aide de Cloud Storage for Firebase. Une fois l'importation terminée, la machine cliente appelle un déclencheur HTTP Cloud Functions for Firebase à l'aide de son jeton Firebase. Cette opération déclenche un processus de serveur qui valide l'envoi, crée un enregistrement de base de données et déplace l'enregistrement vers un répertoire public sur Google Cloud Storage.

Sol solide

L'ensemble de notre contenu public est stocké dans une série de fichiers plats dans un bucket Cloud Storage. Nos données sont ainsi rapidement accessibles dans le monde entier, et nous n'avons pas à nous soucier des charges de trafic importantes qui affectent la disponibilité des données de quelque manière que ce soit.

Nous avons utilisé Firebase Realtime Database et Cloud Functions pour créer un outil de modération/organisation simple qui nous permet d'observer chaque envoi en RV et de publier de nouvelles playlists à partir de n'importe quel appareil.

7. Service Workers

Les service workers constituent une innovation assez récente qui permet de gérer la mise en cache des éléments de sites Web. Dans notre cas, les service workers chargent notre contenu très rapidement pour les visiteurs connus et permettent même au site de fonctionner hors connexion. Ces fonctionnalités sont importantes, car un grand nombre de nos visiteurs utilisent des connexions mobiles de qualité variable.

L'ajout d'un service workers au projet a été facile grâce à un plug-in pratique pack Webpack qui gère la plupart des tâches lourdes à votre place. Dans la configuration ci-dessous, nous générons un service worker qui met automatiquement en cache tous nos fichiers statiques. Elle extrait le dernier fichier de playlist du réseau, s'il est disponible, car la playlist est constamment mise à jour. Tous les fichiers JSON d'enregistrement doivent être extraits du cache s'ils sont disponibles, car ils ne seront jamais modifiés.

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',
        },
        },
    }],
    })
);

Actuellement, le plug-in ne gère pas les éléments multimédias chargés progressivement, comme nos fichiers musicaux. Nous avons donc contourner ce problème en définissant l'en-tête Cloud Storage Cache-Control de ces fichiers sur public, max-age=31536000 pour que le navigateur mette en cache le fichier pendant un an maximum.

Conclusion

Nous sommes impatients de voir comment les artistes vont contribuer à cette expérience et l'utiliser comme un outil d'expression créative par le biais du mouvement. Nous avons publié tout le code Open Source, disponible à l'adresse https://github.com/puckey/dance-tonite. En ces débuts de la RV, et plus particulièrement de WebVR, nous avons hâte de découvrir les orientations créatives et inattendues de ce nouveau support. Dansez le rythme.