J'ai été ravie lorsque l'équipe Google Data Arts a contacté Moniker et moi-même pour une collaboration visant à explorer les possibilités offertes par WebVR. J'ai regardé le travail de leur équipe au fil des ans et leurs projets ont toujours été en phase avec moi. Notre collaboration a donné naissance à Dance Tonite, une expérience de danse en RV en constante évolution avec LCD Soundsystem et ses fans. Voici comment nous y sommes parvenus.
Le concept
Nous avons commencé par développer une série de prototypes utilisant WebVR, une norme ouverte qui permet d'accéder à la réalité virtuelle en accédant à un site Web à l'aide de votre navigateur. L'objectif est de permettre à tous de profiter des expériences de RV plus facilement, quel que soit l'appareil dont vous disposez.
Nous avons pris cela à cœur. Tout ce que nous avons conçu devrait fonctionner avec tous les types de RV, des casques de RV compatibles avec les téléphones mobiles tels que Daydream View de Google, Cardboard et Gear VR de Samsung aux systèmes à grande échelle tels que HTC VIVE et Oculus Rift, qui reflètent vos mouvements physiques dans votre environnement virtuel. Plus important encore, nous avons estimé qu'il serait dans l'esprit du Web de créer une solution qui fonctionne aussi pour tous ceux qui ne possèdent pas d'appareil de RV.
1. Capture de mouvement DIY
Comme nous voulions impliquer les utilisateurs de manière créative, nous avons commencé à examiner les possibilités de participation et d'expression de soi à l'aide de la VR. Nous avons été impressionnés par la précision avec laquelle vous pouvez vous déplacer et regarder autour de vous en réalité virtuelle, et par la fidélité de l'expérience. Cela nous a donné une idée. Au lieu de demander aux utilisateurs de regarder ou de créer quelque chose, pourquoi ne pas enregistrer leurs mouvements ?
Nous avons conçu un prototype dans lequel nous avons enregistré les positions de nos lunettes de réalité virtuelle et de nos contrôleurs pendant que nous dansions. Nous avons remplacé les positions enregistrées par des formes abstraites et nous avons été émerveillés par les résultats. Les résultats étaient si humains et avaient tellement de personnalité ! Nous avons rapidement compris que nous pouvions utiliser WebVR pour réaliser une capture de mouvement à faible coût à domicile.
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 frame par le matériel VR afin que votre code puisse afficher de nouveaux frames à partir du bon point de vue. Grâce à l'API GamePad avec WebVR, nous pouvons également accéder à la position/orientation des contrôleurs des utilisateurs via l'objet GamepadPose. Nous stockons simplement toutes ces valeurs de position et d'orientation à chaque frame, créant ainsi un "enregistrement" des mouvements de l'utilisateur.
2. Minimalisme et costumes
Avec l'équipement de RV à l'échelle de la pièce d'aujourd'hui, nous pouvons suivre trois points du corps de l'utilisateur: sa tête et ses deux mains. Dans "Dance Tonite", nous voulions nous concentrer sur l'humanité dans le mouvement de ces trois points dans l'espace. Pour ce faire, nous avons défini une esthétique aussi minimale que possible afin de nous concentrer sur le mouvement. Nous appréciions l'idée de mettre le cerveau des gens au travail.
Cette vidéo illustrant le travail du psychologue suédois Gunnar Johansson est l'un des exemples auxquels nous nous sommes référés lorsque nous avons envisagé de simplifier au maximum les choses. Il montre comment les points blancs flottants sont immédiatement reconnaissables comme des corps lorsqu'ils sont en mouvement.
Visuellement, nous nous sommes inspirés des salles colorées et des costumes géométriques de cet enregistrement de la recréation de Margarete Hastings en 1970 du ballet Triadic de Oskar Schlemmer.
Alors que Schlemmer a choisi des costumes géométriques abstraits pour limiter les mouvements de ses danseurs à ceux de marionnettes, nous avions l'objectif inverse pour Dance Tonite.
Nous avons finalement basé notre choix de formes sur la quantité d'informations qu'elles transmettaient par rotation. Un orbe est identique quelle que soit la rotation, mais un cône pointe vraiment dans la direction qu'il regarde et est différent de l'avant à l'arrière.
3. Pédale de boucle pour le mouvement
Nous voulions montrer de grands groupes de personnes dansant et bougeant ensemble. Le faire en direct ne serait pas réalisable, car les appareils de RV ne sont pas assez nombreux. Nous voulions toutefois que des groupes de personnes réagissent les uns aux autres par le biais du mouvement. Nous avons pensé à la performance récursive de Norman McLaren dans son œuvre vidéo de 1964 intitulée "Canon".
La performance de McClaren se compose d'une série de mouvements très chorégraphiés qui commencent à interagir les uns avec les autres après chaque boucle. Tout comme une pédale de boucle dans la musique, où les musiciens improvisent avec eux-mêmes en superposant différents morceaux de musique live, nous voulions voir si nous pouvions créer un environnement dans lequel les utilisateurs pourraient improviser librement des versions plus libres de leurs performances.
4. Salles interconnectées
Comme beaucoup de musiques, les titres de LCD Soundsystem sont construits à l'aide de mesures chronométrées avec précision. Leur titre, "Tonite", qui figure dans notre projet, comporte des mesures de 8 secondes exactement. Nous voulions que les utilisateurs effectuent une performance pour chaque boucle de huit secondes du titre. Même si le rythme de ces mesures ne change pas, leur contenu musical le fait. Au fur et à mesure que le titre progresse, il y a des moments avec différents instruments et voix auxquels les interprètes peuvent réagir de différentes manières. Chacune de ces mesures est exprimée sous la forme d'une pièce dans laquelle les utilisateurs peuvent effectuer une performance qui s'y adapte.
Optimisations pour les performances: ne pas supprimer de frames
Créer une expérience de RV multiplate-forme qui s'exécute sur un seul codebase avec des performances optimales pour chaque appareil ou plate-forme n'est pas une mince affaire.
En réalité virtuelle, l'un des effets les plus nauséeux que vous puissiez ressentir est causé par le fait que la fréquence d'images ne suit pas vos mouvements. Si vous tournez la tête, mais que les images que vos yeux voient ne correspondent pas au mouvement ressenti par votre oreille interne, cela provoque une sensation de nausée instantanée. C'est pourquoi nous devions éviter tout retard important de fréquence d'images. Voici quelques optimisations que nous avons implémentées.
1. Géométrie de tampon instanciée
Étant donné que l'ensemble de notre projet n'utilise qu'une poignée d'objets 3D, nous avons pu améliorer considérablement les performances en utilisant la géométrie de tampon d'instance. Fondamentalement, cela vous permet d'importer votre objet sur le GPU une seule fois et de dessiner autant d'instances de cet objet que vous le souhaitez en 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é le fork expérimental et en cours de Dusan Bosnjak qui implémente THREE.InstanceMesh
, ce qui facilite grandement le travail avec la géométrie de tampon d'instance.
2. Éviter le garbage collector
Comme de nombreux autres langages de script, JavaScript libère automatiquement la mémoire en déterminant les objets alloués qui ne sont plus utilisés. Ce processus est appelé "nettoyage de la 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 se présenter à nos portes à tout moment et commencer à vider les déchets, ce qui entraîne des pertes d'images pendant qu'elles prennent leur temps.
La solution consiste à produire le moins de déchets possible en recyclant nos objets. Au lieu de créer un objet vectoriel pour chaque calcul, nous avons marqué des objets de travail pour les réutiliser. Étant donné que nous les conservons en déplaçant notre référence en dehors de notre champ d'application, elles n'ont pas été marquées pour suppression.
Par exemple, voici notre code pour convertir la matrice de position de la tête et des mains de l'utilisateur en tableau de valeurs de position/rotation que nous stockons à chaque frame. En réutilisant SERIALIZE_POSITION
, SERIALIZE_ROTATION
et SERIALIZE_SCALE
, nous évitons l'allocation de mémoire et le garbage collection qui auraient lieu si nous créions de nouveaux objets à chaque fois que la fonction est appelée.
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 de la lecture animée et progressive...
Pour capturer les mouvements des utilisateurs en réalité virtuelle, nous devions sérialiser la position et la rotation de leur casque et de leurs contrôleurs, puis importer ces données sur nos serveurs. Nous avons commencé par capturer les matrices de transformation complètes pour chaque frame. Les performances étaient bonnes, mais avec 16 nombres multipliés par trois positions chacun à 90 images par seconde, cela a généré des fichiers très volumineux et donc de longs temps d'attente lors de l'importation et du téléchargement des données. En n'extrayant que les données de position et de rotation des matrices de transformation, nous avons pu réduire ces valeurs de 16 à 7.
Étant donné que les visiteurs sur le Web cliquent souvent sur un lien sans savoir exactement à quoi s'attendre, nous devons afficher rapidement du contenu visuel, sinon ils quitteront le site en quelques secondes.
C'est pourquoi nous voulions nous assurer que notre projet pouvait commencer à jouer dès que possible. Au départ, 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 progressiste.
Pour que le projet Dance Tonite s'affiche à la fréquence d'images la plus élevée possible, le navigateur ne dispose que d'un court laps de temps pour effectuer les calculs JavaScript à chaque frame. Si vous prenez trop de temps, les animations commencent à saccades. Au début, nous avons rencontré des problèmes de stuttering, car ces énormes fichiers JSON étaient décodés par le navigateur.
Nous avons découvert un format de données de streaming pratique appelé NDJSON (JSON délimité par un retour à la ligne). L'astuce ici 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 qu'elles ne soient entièrement chargées.
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 différents frames 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 étale le traitement nécessaire sur le temps.
4. Interpoler un mouvement
Comme nous espérions afficher 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 s'est penchée sur le même problème dans le cadre de son projet Virtual Art Sessions (sessions artistiques virtuelles), au cours duquel elle lit des enregistrements d'artistes peignant en RV à l'aide de Tilt Brush. Pour résoudre le problème, l'équipe a créé des versions intermédiaires des données utilisateur avec des fréquences d'images plus faibles et a procédé à une interpolation entre les images lors de leur lecture. Nous avons été surpris de constater que nous pouvions difficilement faire la différence entre un enregistrement interpolé à 15 FPS et l'enregistrement original à 90 FPS.
Pour vous en rendre compte, vous pouvez forcer Dance Tonite à lire les données à différents débits à 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é temporelle 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 sous forme de 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 lire, nous devons connaître la durée actuelle de la musique à la milliseconde près. Il s'avère que, 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 en synchronisation avec la boucle de frame du navigateur. Il est toujours un peu décalé. Parfois, une fraction de milliseconde trop tôt, parfois une fraction trop tard.
Cela entraîne des à-coups dans nos beaux enregistrements de danse, ce que nous voulons éviter à tout prix. Pour y remédier, nous avons implémenté notre propre minuteur en JavaScript. De cette façon, nous pouvons être sûrs que le temps qui s'écoule entre les images correspond exactement au temps écoulé depuis la dernière image. Chaque fois que notre minuteur est désynchronisé de plus de 10 ms avec la musique, nous le synchronisons à nouveau.
6. Élimination et brouillard
Chaque histoire a besoin d'une bonne fin, et nous voulions proposer quelque chose de surprenant aux utilisateurs qui ont atteint la fin de notre expérience. En quittant la dernière pièce, vous entrez dans ce qui ressemble à un paysage paisible de cônes et de cylindres. "Est-ce la fin ?", vous vous demandez. À mesure que vous vous éloignez du champ, les tons de la musique font soudainement apparaître des danseurs dans différents groupes de cônes et de cylindres. Vous vous retrouvez au milieu d'une grande fête ! Puis, lorsque la musique s'arrête brusquement, tout tombe au sol.
Bien que cela ait été agréable pour les spectateurs, cela a entraîné des problèmes de performances à résoudre. Les appareils de RV à grande échelle et leurs stations de jeu haut de gamme ont parfaitement fonctionné avec les 40 performances supplémentaires nécessaires pour notre nouvelle fin. Toutefois, les fréquences d'images sur certains appareils mobiles ont été divisées par deux.
Pour y remédier, nous avons introduit un brouillard. Après une certaine distance, tout devient progressivement noir. Comme nous n'avons pas besoin de calculer ni de dessiner ce qui n'est pas visible, nous éliminons les performances dans les pièces qui ne sont pas visibles, ce qui nous permet d'économiser du travail pour le processeur et le GPU. Mais comment choisir la bonne distance ?
Certains appareils peuvent gérer tout ce que vous leur demandez, tandis que d'autres sont plus limités. Nous avons choisi d'implémenter un barème dégressif. En mesurant continuellement le nombre d'images par seconde, nous pouvons ajuster la distance de notre brouillard en conséquence. Tant que la fréquence d'images fonctionne correctement, nous essayons de prendre en charge davantage de tâches de rendu en repoussant le brouillard. Si le framerate n'est pas assez fluide, nous rapprochons le brouillard, ce qui nous permet d'ignorer 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: créer de la RV pour le Web
Concevoir et développer des expériences asymétriques multiplates-formes implique de tenir compte des besoins de chaque utilisateur en fonction de son appareil. Et pour chaque décision de conception, nous devions voir comment cela pourrait avoir un impact sur les autres utilisateurs. Comment faire pour s'assurer que ce que vous voyez en RV est tout aussi intéressant que sans RV, et inversement ?
1. L'orbe jaune
Les utilisateurs de la RV à l'échelle de la pièce effectueraient donc les performances, mais comment les utilisateurs d'appareils de RV mobile (tels que Cardboard, Daydream View ou Samsung Gear) pourraient-ils profiter du projet ? Pour ce faire, nous avons ajouté un nouvel élément à notre environnement: l'orbe jaune.
Lorsque vous regardez le projet en VR, vous le faites du point de vue de l'orbe jaune. Lorsque vous passez d'une pièce à l'autre, les danseurs réagissent à votre présence. Il vous fait des signes, danse autour de vous, fait des mouvements amusants derrière votre dos et s'écarte rapidement pour ne pas vous heurter. L'orbe jaune est toujours au centre de l'attention.
En effet, lorsque vous enregistrez une performance, l'orbe jaune se déplace au centre de la pièce en même temps que la musique et revient en boucle. La position de l'orbe donne au vidéaste une idée de l'endroit où il se trouve dans le temps et du temps qu'il lui reste dans sa boucle. Il leur fournit un axe naturel autour duquel développer leurs performances.
2. Autre point de vue
Nous ne voulions pas écarter les utilisateurs sans la réalité virtuelle, d'autant plus qu'ils constituent probablement notre plus large audience. Plutôt que de créer une fausse expérience de RV, nous voulions offrir aux appareils à écran leur propre expérience. Nous avons eu l'idée de montrer les performances d'en haut, d'une perspective isométrique. Cette perspective a une longue 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 la RV sont au cœur de l'action, la perspective isométrique offre une vue d'ensemble de l'action. Nous avons choisi d'agrandir légèrement les modèles, ce qui leur donne une touche d'esthétique de maison de poupée.
3. Ombres: faites semblant jusqu 'à ce que vous y arriviez
Nous avons constaté que certains de nos utilisateurs avaient du mal à voir la profondeur dans 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.
Il s'avère que créer des ombres en 3D est difficile. en particulier pour les appareils à écran réduit, comme les téléphones mobiles. Au départ, 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 pirate informatique expérimenté Mr. doob, il a eu l'idée originale de… les simuler.
Au lieu de calculer comment chacun de nos objets flottants masque nos lumières et projette donc des ombres de différentes formes, nous dessinons la même image de texture floue circulaire sous chacun d'eux. Nos visuels n'essayant pas d'imiter la réalité au départ, nous avons découvert que nous pouvions nous en sortir assez facilement en quelques ajustements. Lorsque les objets se rapprochent du sol, les textures deviennent plus sombres et plus petites. Lorsqu'ils se déplacent vers le haut, nous rendons les textures plus transparentes et plus grandes.
Pour les créer, nous avons utilisé cette texture avec un dégradé doux du blanc au noir (sans transparence alpha). Nous définissons le matériau comme transparent et utilisons un mélange soustractif. Cela permet de les mélanger harmonieusement 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 danseur, les visiteurs sans casque de RV peuvent regarder les choses du point de vue du danseur. Sous cet angle, de nombreux petits détails deviennent apparents. Pour essayer de garder leur chorégraphie en phase, les danseurs se regardent rapidement. Lorsque l'orbe entre dans la pièce, vous le voyez regarder nerveusement dans sa direction. En tant que spectateur, vous ne pouvez pas influencer ces mouvements, mais ils transmettent un sentiment d'immersion étonnamment efficace. Encore une fois, nous avons préféré procéder ainsi plutôt que de proposer à nos utilisateurs une version de VR factice contrôlée par la souris.
5. Partager des enregistrements
Nous savons à quel point vous pouvez être fier d'un enregistrement minutieusement chorégraphié de 20 niveaux d'artistes qui réagissent les uns aux autres. Nous savions que nos utilisateurs voudraient probablement le montrer à leurs amis. Mais une image fixe de cette prouesse ne communique pas assez. Nous souhaitions plutôt permettre à nos utilisateurs de partager des vidéos de leurs performances. En fait, pourquoi ne pas créer un GIF ? Nos animations sont à ombres plates, ce qui est parfait pour les palettes de couleurs limitées du format.
Nous avons utilisé GIF.js, une bibliothèque JavaScript qui vous permet d'encoder des GIF animés depuis le navigateur. Il transfère l'encodage des frames aux nœuds de travail Web, qui peuvent s'exécuter en arrière-plan en tant que processus distincts, et peuvent ainsi tirer parti de plusieurs processeurs travaillant côte à côte.
Malheureusement, avec le nombre d'images nécessaires pour les animations, le processus d'encodage était encore trop lent. Le GIF peut créer de petits fichiers en utilisant une palette de couleurs limitée. Nous avons constaté que la plupart du temps était consacrée à trouver la couleur la plus proche pour chaque pixel. Nous avons pu optimiser ce processus dix fois en implémentant un petit raccourci : si la couleur du pixel est identique à la dernière, utilisez la même couleur de la palette qu'auparavant.
Nous avions des encodages rapides, mais les fichiers GIF obtenus étaient trop volumineux. Le format GIF vous permet d'indiquer comment chaque frame doit être affichée 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 à chaque frame, nous ne mettons à jour que les pixels qui ont changé. Bien que cela ralentisse à nouveau le processus d'encodage, cela a permis de réduire considérablement la taille de nos fichiers.
6. Terre ferme : Google Cloud et Firebase
Le backend d'un site de "contenus générés par les utilisateurs" peut souvent être compliqué et fragile, mais nous avons conçu un système simple et robuste grâce à Google Cloud et Firebase. Lorsqu'un danseur 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. Cela déclenche un processus 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.
Tous nos contenus publics sont stockés dans une série de fichiers plats dans un bucket Cloud Storage. Cela signifie que nos données sont rapidement accessibles dans le monde entier et que les charges de trafic élevées n'affectent en aucun cas la disponibilité des données.
Nous avons utilisé une base de données Firebase Realtime Database et des points de terminaison Cloud Functions pour créer un outil de modération/sélection simple qui nous permet de regarder chaque nouvelle soumission en RV et de publier de nouvelles playlists à partir de n'importe quel appareil.
7. Service workers
Les service workers sont une innovation assez récente qui permet de gérer la mise en cache des composants d'un site Web. Dans notre cas, les services workers chargent nos contenus à la vitesse de l'éclair pour les visiteurs réguliers et permettent même au site de fonctionner hors connexion. Ces caractéristiques sont importantes, car bon nombre de nos visiteurs utilisent une connexion mobile de qualité variable.
L'ajout de services workers au projet a été facile grâce à un plug-in webpack pratique 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. Il extrait le dernier fichier de playlist du réseau, le cas échéant, car la playlist est mise à jour en permanence. Tous les fichiers JSON d'enregistrement doivent être extraits du cache, le cas échéant, car ils ne changeront jamais.
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, tels que nos fichiers musicaux. Pour contourner ce problème, nous avons défini l'en-tête Cloud Storage Cache-Control
sur ces fichiers sur public, max-age=31536000
, de sorte que le navigateur mette en cache le fichier pendant une durée maximale d'un an.
Conclusion
Nous avons hâte de voir comment les artistes vont enrichir cette expérience et l'utiliser comme outil d'expression créative grâce au mouvement. Nous avons publié tout le code Open Source, que vous pouvez consulter sur https://github.com/puckey/dance-tonite. En ces débuts de la RV, et plus particulièrement du WebVR, nous sommes impatients de voir quelles seront les perspectives créatives et inattendues de ce nouveau support. Dansez !