-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathatom.xml
More file actions
550 lines (267 loc) · 353 KB
/
atom.xml
File metadata and controls
550 lines (267 loc) · 353 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Binomed Blog</title>
<icon>https://www.gravatar.com/avatar/c4da9d3a9bfb1bee978f8c3422ca9a32</icon>
<subtitle>Another G33k blog</subtitle>
<link href="http://jef.binomed.fr/atom.xml" rel="self"/>
<link href="http://jef.binomed.fr/"/>
<updated>2024-02-01T14:22:37.542Z</updated>
<id>http://jef.binomed.fr/</id>
<author>
<name>jefBinomed</name>
<email>jean.francois.garreau+blog@gmail.com</email>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>Père JF, raconte nous le DevFest Nantes - L'après</title>
<link href="http://jef.binomed.fr/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-8/"/>
<id>http://jef.binomed.fr/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-8/</id>
<published>2024-01-31T13:08:00.000Z</published>
<updated>2024-02-01T14:22:37.542Z</updated>
<content type="html"><![CDATA[<p><img src="/assets/2024-01-DevFestNantesPereCastor/devfest-nantes.jpeg" alt="Image du Logo du DevFest Nantes"></p><p>Ça y est le moment clé était arrivé ! La 10ième édition était passée… Que faire par la suite ? Arrêter ? Après tout, cela fait 11 ans que j’anime cette communauté, n’est-il pas venu le temps d’arrêter et de prendre une retraite communautaire du point de vue de l’organisation…? Cette question, je me la suis posée et très sérieusement. Cette édition était pour moi un passage obligatoire à travers lequel je voulais et je devais passer ! Cette pause potentielle était donc une question légitime et saine.</p><p>2022 fut aussi une édition où une fois de plus le code of conduct n’avait pas été respecté par certains participants et où cela nous avait une fois de plus dérangés au plus au point. En effet, depuis 2015 nous mettons en place un code of conduct et nous étions ainsi un événement précurseur dans ce domaine jusqu’à revoir complètement ce dernier en 2019 suite à une attaque peu constructive qui nous qualifiait d’organisateurs indignes qui ne devraient pas avoir le droit d’organiser un événement avec un tel code of conduct (véridique 🥲) . Cette évolution du code of conduct fut nécessaire et aussi un tournant encore plus fort dans notre engagement en faveur de l’inclusivité dans notre événement (mais ça aussi, c’est encore une autre histoire 😃). Cependant, en 2022, cette nouvelle attaque nous a amenés à nous remettre en question et à faire ce que nous faisons le mieux : innover !</p><p>Welcome to DevFest Safe ! C’est ainsi qu’est née cette idée nouvelle d’amener une squad tournante pendant l’événement, sorte de “garant·es” du respect des règles de bien séance dans notre événement. Mais je reviendrais sur ça plus loin.</p><p>Il n’en fallait pas plus pour me donner l’envie de rester pour re-signer pour encore quelques années ! Ça, plus le fait d’amener toujours plus de nouveautés, de fun et d’expérience à ce bel événement qu’est le DevFest Nantes.</p><p>La première décennie du DevFest Nantes aura ouvert la voie à bon nombre d’événements français sur une autre façon d’organiser des événements techs. Maintenant, l’écosystème Français est plein de conférences de très bonnes qualités où chaque organisateurs·rices redoublent d’idées pour innover, proposer une expérience riche et agréable pour les participant·es et nous sommes fiers d’y avoir contribué.</p><p>De plus, nous savions que nous faisions quelque chose de bien mais nous avions du mal à être objectifs. Puis en juin 2023, lors d’un summit organisé par Google à Amsterdam, l’award tombe : le DevFest Nantes est sacré meilleur événement de l’année pour la deuxième année consécutive.</p><p><img src="/assets/2024-01-DevFestNantesPereCastor/event-of-the-year.jpeg" alt="Award de google pour "l'event of the year""></p><p>Que faire ensuite ? S’arrêter là ? Pourquoi ? Pourquoi devrions-nous nous arrêter en chemin ? En fait, l’ADN du DevFest Nantes, c’est ce mélange subtil de fun, de tech, de bienveillance, d’attentions multiples, … Les idées, heureusement, ce n’est pas ça qui manque ! Et nous comptons bien continuer d’innover à notre manière. Il y a des sujets et des débats dans lesquels nous ne voudrons jamais rentrer tout comme il y a des combats que nous voulons mener et sur lesquels nous nous engageons. C’est donc ça, ce que va être les prochaines années du DevFest.</p><h1 id="L’apres-10-ans"><a href="#L’apres-10-ans" class="headerlink" title="L’après 10 ans"></a>L’après 10 ans</h1><p>2023 fut donc stratégique pour nous, que faire suite à cette édition des 10 ans, hors norme où nous prenions toute la cité… Les retours participant·es étaient au rendez-vous, ce grand auditorium remplissait vraiment son objectif initial : permettre à toutes et tous d’assister aux conférences qu’ils et elles souhaitaient ! Le hic, tout ce que nous avions mis en oeuvre pour cette édition (2022) nous fait avoir une édition déficitaire pour la première fois de l’histoire du DevFest… Nous avons donc fait un choix : augmenter légèrement et les sponsors (où les prix n’avaient pas augmenté depuis plusieurs années) et ne pas toucher aux places participant·es. Cette manoeuvre fut un pari où nous allions tester la solidité du DevFest face à nos partenaires. Au final, nous avons eu raison car nos packs se sont vendus de manière instantaneé !</p><p>Rassurés, nous avons pu mettre en oeuvre nos plans et nos nouveautés ou ajustements tels :</p><ul><li>Mise en place de l’équipe DevFest Safe</li><li>Poursuite de la vélotypie mais cette fois sponsorisée</li><li>Suppression de totebags et gobelets car l’impact écologiques était trop mauvais</li><li>Refonte de notre système de gestion de billetterie afin de faciliter la vie de nos sponsors et participant·es</li></ul><h2 id="DevFest-Safe"><a href="#DevFest-Safe" class="headerlink" title="DevFest Safe"></a>DevFest Safe</h2><p>Parmis les gros projets de 2023, l’équipe DevFest Safe fut une de nos “grosses” nouveautés sur cette édition.</p><p><img src="/assets/2024-01-DevFestNantesPereCastor/devfest-safe.jpeg" alt="L'équipe de bénévoles DevFest Safe (8 personnes derrière des lettres géantes du DevFest)"></p><p>Cette équipe a un fonctionnement inspiré de ce qui peut exister dans certains festivals de musiques où des personnes “patrouillent” pendant l’événement afin de s’assurer que tout se passe bien et que nous puissions accompagner un·e participant·e en situation inconfortable voir pire.</p><p>Ceci fut l’occasion de se pencher sur des supports de formation, travailler une communication spécifique etc.</p><p>Le bilan de la mise en place de cette équipe est positif ! Non pas par ce qu’aucuns faits ne nous ai été reporté nécessitant une intervention mais c’était une réussite par ce que la mise en place de cette équipe a fait parler ! À la fois dans les couloirs mais aussi, des personnes sont venues à nous nous proposant des axes d’améliorations pour le fonctionnement de cette équipe ! C’est donc avec joie que je sais que cette équipe sera renouvelée l’année prochaine mais avec en plus de nouvelles perspectives pour rendre le DevFest le plus proche possible d’une Safe place. Car soyons objectifs, nous ne pourrons jamais garantir le zéro incident… mais par contre, nous pouvons faire en sorte que ce type d’actions soient connues, que nous sensibilisions et que nous sachions comment réagir.</p><p>Je pourrais aussi revenir sur tout ce qu’on a mis en oeuvre et tous nos projets et réflexions sur ce qu’est un événement inclusif, mais ça, c’est pour une autre histoire …</p><h1 id="Suis-je-toujours-creatif…"><a href="#Suis-je-toujours-creatif…" class="headerlink" title="Suis-je toujours créatif…?"></a>Suis-je toujours créatif…?</h1><p>2023 fut pour moi aussi un test… Suis-je encore capable d’être créatif après toutes ces années, comment continuer de créer la surprise sur les keynotes… ? Heureusement le thème nous aide chaque année et c’est ainsi que cette année, j’ai pu trouver une keynote d’ouverture qui restera sûrement comme l’une des meilleurs que nous avons eu jusqu’à présent (n’hésitez pas à aller la voir sur <a href="https://www.youtube.com/watch?v=7GgLvCngX2k&list=PLuZ_sYdawLiUHU4E1i5RrFsRN_lQcgPwT&ab_channel=GDGFrance">youtube</a>) et j’ai pu créer et un jeu complètement nouveau tout droit sorti de ma tête avec l’aide précieuse de personnes de l’équipe et quelques personnes extérieures pour lui donner vie. Nous avons ainsi créé PopCorn Talk.</p><p><img src="/assets/2024-01-DevFestNantesPereCastor/popcorn-talk.jpeg" alt="Photo de la keynote de cloture popcorn talk (2 énorme pots de pop-corn avec 3 personnes et au milieu, un présentateur)"></p><p>Dans les anecdotes drôles de cette keynote de cloture, Aymeric qui a créé de toutes pièces les 2 énormes pots de pop-corns nous servant de buzzers a fait cuire 20Kg de popcorns chez lui et sa maison a senti le po-pcorn pendant plus d’un mois ! C’est le genre de choses qui me fait vibrer et me motive à organiser cet événement.</p><p>Aujourd’hui, si on me demande si je veux continuer à organiser le DevFest Nantes, je répondrai clairement : “OUI !”. Pour combien de temps, ça je ne le sais pas encore mais tant que, avec cette équipe, nous continuerons de chercher à proposer des choses nouvelles aux participant·es, à créer des keynotes d’ouvertures, des comptes à rebours ou des keynotes de cloture loufoque.</p><p>En conclusion, le DevFest aura à jamais marqué ma vie et restera ce projet un peu fou de 3 amis qui devient quelque chose de grand ; attire à lui des personnes de qualités (toute l’équipe actuelle du GDG) ; se remet en question et est évolution constante. Je sais que cette histoire n’est pas encore finie mais ça, ça sera pour une autre fois…</p><h1 id="Pour-aller-plus-loin"><a href="#Pour-aller-plus-loin" class="headerlink" title="Pour aller plus loin"></a>Pour aller plus loin</h1><p>Cet article est issue d’une série d’articles sur l’organisation du DevFest Nantes. Vous pouvez retrouver les autres articles ici :</p><ul><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-1/">Père JF, raconte nous le DevFest Nantes - La genèse</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-2/">Père JF, raconte nous le DevFest Nantes - Les premières éditions, l’ère Epitech</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-3/">Père JF, raconte nous le DevFest Nantes - Un nouveau lieu, de nouveaux challenges</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-4/">Père JF, raconte nous le DevFest Nantes - Un évènement grandissant</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-5/">Père JF, raconte nous le DevFest Nantes - On pousse les murs encore une fois !</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-6/">Père JF, raconte nous le DevFest Nantes - L’équipe et sa taille</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-7/">Père JF, raconte nous le DevFest Nantes - Les 10 ans</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-8/">Père JF, raconte nous le DevFest Nantes - L’après</a></li></ul>]]></content>
<summary type="html"><p><img src="/assets/2024-01-DevFestNantesPereCastor/devfest-nantes.jpeg" alt="Image du Logo du DevFest Nantes"></p>
<p>Ça y est le moment c</summary>
<category term="Event" scheme="http://jef.binomed.fr/categories/Event/"/>
<category term="Devfest" scheme="http://jef.binomed.fr/tags/Devfest/"/>
<category term="Organisation" scheme="http://jef.binomed.fr/tags/Organisation/"/>
</entry>
<entry>
<title>Père JF, raconte nous le DevFest Nantes - Les 10 ans</title>
<link href="http://jef.binomed.fr/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-7/"/>
<id>http://jef.binomed.fr/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-7/</id>
<published>2024-01-31T13:07:00.000Z</published>
<updated>2024-02-01T14:21:29.164Z</updated>
<content type="html"><![CDATA[<p><img src="/assets/2024-01-DevFestNantesPereCastor/devfest-nantes.jpeg" alt="Image du Logo du DevFest Nantes"></p><p>Les 10 ans !!! Enfin ! Cette édition était dans nos têtes depuis déjà de nombreuses années et la voilà !</p><p><img src="/assets/2024-01-DevFestNantesPereCastor/devfest10ans.jpeg" alt="Image de la cité des congrès avec la virtophanie de l'édition des 10 ans"></p><p>En effet, depuis presque 4 ans, nous commencions à penser à cette édition des 10 ans, en commençant par le thème. Je me souviens : Rex du DevFest 2018, comme chaque année, nous réfléchissons au thème de l’année suivante quand tout à coup, un thème sort : “Jules Verne” ! À ce moment-là, toute l’équipe était unanime, ce thème est trop bien car correspond à l’identité forte de Nantes et doit donc être notre thème des 10 ans ! Ainsi petit à petit, des idées viennent se rajouter pour cette édition spéciale et sur comment la rendre à part. C’est de cette manière que nous avons travaillé notre budget de façon à nous permettre de faire une édition qui serait hors norme pour nous, années après années.</p><p>Mais concrètement, à quoi a ressemblé cette 10ième édition ? Cette édition fut surtout l’occasion pour nous de grossir pour la dernière fois en prenant TOUTE la cité des congrès afin de proposer aux participant·es la possibilité d’entrer dans toutes les salles sans se faire rejeter. En effet, nous avons ainsi pris la salle nommée grand Auditorium que nous avons renommée “Jules Verne” pour cette édition. Cette dernière nous offre une capacité d’accueil de 1200 places !</p><p><img src="/assets/2024-01-DevFestNantesPereCastor/grand-auditorium.jpeg" alt="vu de la salle "Jules Verne" remplie (gros amphi-théatre de 1200 places avec sur scène un gros écran, 2 speakers)"></p><p>Cette salle a, à elle seule représenté pour nous beaucoup de nouveautés : une scénographie différente à penser, un écran large, une régie différente, … Bref, de beaux enjeux pour nous encore cette fois.</p><p>En vrac, la 10ième édition c’était aussi :</p><ul><li>Nous avons créé une vidéo d’intro un peu particulière car elle reprenait notre histoire. Chose que nous n’avions pas faite avant.</li><li>Nous avons fait venir des ancien·nes top speaker pour avoir une sorte de “Best Of” du DevFest Nantes.</li><li>Nous sommes allés chercher un youtuber pour ouvrir le DevFest avec une keynote.</li><li>Nous avons mis en place de la vélotypie (transcription live des talks).</li><li>Nous avons eu une animation escape game (en soi déjà en place depuis quelques années)</li><li>Nous avons eu une after party avec concert (en soi déjà en place depuis quelques années)</li></ul><iframe width="560" height="315" src="https://www.youtube.com/embed/mO9qbVavCuQ?si=F0MvarUM74oAlusy" title="Vidéo de la keynote d'ouverture qui fait la rétrospective de nos 10 dernières années" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>Vidéo de la keynote d'ouverture qui fait la rétrospective de nos 10 dernières années<h1 id="Salle-Jules-Verne"><a href="#Salle-Jules-Verne" class="headerlink" title="Salle Jules Verne"></a>Salle Jules Verne</h1><p>Cette salle étant dans une dimension que nous ne connaissions pas, il a fallu nous adapter ! Déjà notre plus grande salle (Titan) avait été à l’époque un gros challenge car nous ne pouvions pas laisser cette dernière sans déco. Nous avions donc découvert à cette époque ces enjeux. Mais là, c’était encore plus flagrant ! En effet, le grand auditorium de la cité des congrès est conçu pour des spectacles et pas des salles de conférences ! Comment procéder ? Comment mettre en scène tout ça ? Comment projeter de manière qualitative une conférence dans une telle salle ? N’aurons-nous pas un effet “salle vide” ? Beaucoup de questions mais encore une fois, notre prestataire vidéo, MStream a su nous accompagner et nous proposer des solutions nous correspondant !</p><p>Ainsi, nous avons travaillé une scène avec un gros écran pour prendre de la place sur scène. Une des particularités de ce dernier était qu’en son point central un espace était dédié à la projection de conférence avec des gabarits animés autour permettant de mettre en avant tantôt le contenu, tantôt le·a speaker.</p><p>De même, nous avons rajouté plusieurs petits écrans Led pour “meubler” la scène et ainsi créer des animations entre les conférences. Et pour finir, nous gardions le concept disponible en salle Titan, des écrans latéraux permettant d’afficher en grand le·a speaker pour les personnes les plus éloignées.</p><p><img src="/assets/2024-01-DevFestNantesPereCastor/sceno-jules-vernes.jpeg" alt="Exemple de scénographie en salle Jules Verne (un écran principal avec un info décor et en son centre, la projection d'un écran)"></p><p>Mstream nous a aussi permis de solutionner la solution de l’écran car en premier lieu, la cité des congrès ne nous proposait qu’un énorme vidéo-projecteur ce qui aurait donné un effet vide sur l’espace d’affichage. La solution retenue est en fait un double vidéo projecteur caché derrière la scène qui permet ainsi de balayer une zone horizontale très large et donc d’avoir l’effet comme sur la photo ci-dessus.</p><p>Enfin concernant le monde et l’effet “vide”, cette salle possède des balcons sur le côté et en hauteur qui nous permettent de gérer plus facilement les flux et d’amenner les gens plus facilement à se concentrer en un point de la salle. Grâce à cela, le “panier” (zone d’accueil principal) compte environ 800 sièges et donc cette zone est facilement remplie comme pour Titan évitant l’effet que l’on redoutait.</p><h1 id="Keynote-avec-device-Orchestra"><a href="#Keynote-avec-device-Orchestra" class="headerlink" title="Keynote avec device Orchestra"></a>Keynote avec device Orchestra</h1><p>Cette keynote fut pour nous l’occasion de tester quelque chose de nouveau. Faire venir un youtubeur avec des contraintes techniques assez forte. En effet, <a href="https://www.youtube.com/@DeviceOrchestra">Device Orchestra</a> est une chaine youtube où son créateur reprend des morceaux connus et les reproduit avec des brosses à dents, des TPE, des imprimantes ou grilles pains 😅.</p><p><img src="/assets/2024-01-DevFestNantesPereCastor/device-orchestra.jpeg" alt="Photo de l'installation de device orchestra au devfest avec 2 brosses à dents et un TPE"></p><p>Ce qui était drôle, c’est que c’était une première pour nous mais aussi pour lui finalement. Même s’il anime sa chaine youtube depuis un moment, il n’avait jamais fait de prestation publique ! C’est donc posé la question à 1 million : “Combien coûte un youtuber à plus de 100 000 followers…?” La réponse restera secrète mais ce que je peux vous dire c’est que c’est raisonnable ☺️.</p><p>Se pose ensuite la question de la prestation, idéalement, nous souhaitons faire jouer ses “instruments” sur scène mais que prendre comme micros, comment disposer ça sur scène, … Autant de questions qui nous font avoir des discussions intéressantes avec la cité qui a été surpris par notre demande : “Comment on fait pour capter le son d’une brosse à dent et d’un TPE 🤣?”</p><p>Une fois ces “détails” réglés, nous avons fait notre “lettre au père-noël” en lui expliquant quel était le public, quelles attentes nous pouvions avoir et notamment si il lui était possible d’adapter “Thunderstruck de AC/DC”… Par chance, il est allé dans toutes nos demandes et il a pu démarrer sa keynote avec le morceau d’AC/DC qui fut salué par toute la salle ! Un an après, il a même publié une version complète de ce morceau finalement créé spécialement pour nous 😇</p><iframe width="560" height="315" src="https://www.youtube.com/embed/BJbV7Lr0usY?si=6lkdJ2hI6M5RUpeE" title="Vidéo Thundestruck de AC/DC fait par device orchestra" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>Vidéo Thundestruck de AC/DC fait par device orchestra<p>Une des anecdote marrante de cette keynote est qu’à un moment pendant la conférence, il croyait qu’une vidéo ne se lançait pas car il voulait nous expliquer le fonctionnement de transposition électronique du chant, il s’est donc lancé dans un moment a cappella qui a, à la fois surpris et conquis le public 🎤</p><h1 id="Mise-en-place-de-la-velotypie"><a href="#Mise-en-place-de-la-velotypie" class="headerlink" title="Mise en place de la vélotypie"></a>Mise en place de la vélotypie</h1><p>La mise en place de vélotypie ou transcription live des conférences est un sujet qui nous tenait à coeur depuis de nombreuses années… La première fois que nous avions eu envie de mettre ça en place remonte à 2018. J’avais contacté une entreprise Parisienne qui faisait la transcription de l’événement ‘Paris Web’. Hors le budget annoncé était vraiment déraisonnable ! J’ai donc creusé et je me suis intéressé à la solution mise en place par l’événement “MixIT” à Lyon. Ce dernier à cette époque-là se reposait sur la solution <a href="https://www.scribovox.fr/">scribovox</a>. Cette solution se base sur de la reconnaissance vocale disponible via une API Google couplée à une base de données realtime “firebase”. La clé de la qualité de sa solution repose sur le fait que des bénévoles soient mobilisé·es pour corriger les fautes de reconnaissance automatique.</p><p>Ceci nous a donc permis de comprendre que 2 mondes s’opposaient radicalement faisant s’affronter la machine et l’humain. En effet, d’un côté, nous avons de la reconnaissance vocale automatisée par des machines qui nous permet d’avoir un résultat très rapide en termes de latence (environ 2s) mais entraîné pour le langage de tous les jours et dans une langue bien précise. De l’autre une personne qui tape ce qu’elle entend et sera en mesure d’être au plus proche de la réalité mais avec une latence supérieure (environ 5s).</p><p>Si l’on veut avoir un mécanisme fiable avec la traduction automatique, nous devons rajouter une phase de correction manuelle (solution scribovox) qui au final arrive à la même latence…</p><p>En conclusion, s’opposait, d’un côté une solution hors de prix mais fiable et de l’autre une solution quasiment gratuite mais demandant une armée de bénévole pour être fiable… Dans les 2 cas, ces solutions n’étaient pas jouables car il nous paraissait impensable de ne mettre en oeuvre la solution que dans une salle !</p><p>Finalement, la solution est arrivée par le nord puisque c’est grâce au DevFest Lille que nous avons trouvé notre prestataire. À Lille, un acteur de ce marché avec des tarifs beaucoup plus abordables (4 fois moins cher que la solution parisienne) a fait la transcription live du DevFest Lille 2021 ! Nous avons donc pris contact avec cette société et nous avons pu ainsi mettre en place ENFIN cette solution au DevFest</p><p><img src="/assets/2024-01-DevFestNantesPereCastor/velotypie.jpeg" alt="photo de la grande scène du devfest avec de la vélotypie"></p><p>Le challenge reste maintenant à éduquer nos speaker·euses pour fournir en avance les textes utilisés, à parler plus lentement, … De cette manière, cela pourra permettre une transcription la plus qualitative possible. À titre d’exemple, lors d’une conférence où un speaker parlait vraiment trop vite, la personne à l’autre bout a été obligée de taper “Désolé, il parle trop vite, j’arrive pas à suivre”. De plus, il est vraiment important pour nos speakers de donner les termes techniques car quand on ne travaille pas dans l’informatique, comment savoir que les sons phonétiques “do·keur” correspondent au terme technique Docker. Bref, ceci reste une piste d’amélioration pour nous mais c’était quand même un succès pour nous.</p><h1 id="Keynote-de-cloture-improvisee"><a href="#Keynote-de-cloture-improvisee" class="headerlink" title="Keynote de cloture improvisée !"></a>Keynote de cloture improvisée !</h1><p>Pour terminer ce DevFest, nous ne pouvions pas terminer tels que les gens nous attendaient, à savoir, faire une parodie de jeu télévisé… Nous avons donc fait appel à une société qui “improvise” une session de fermeture. Pour ce faire, leur concept est à la fois simple et original, ils et elles s’intègrent à l’événement, suivent les conférences pendant 2 jours et prennent des notes sur ce qu’ils et elles ont entendu.</p><p>Avec ces notes, ils et elles définissent un semblant d’histoire avec des moments “forts” à évoquer mais où tout le contenu est lui improvisé !</p><p><img src="/assets/2024-01-DevFestNantesPereCastor/keynote-cloture-22.jpeg" alt="image de la keynote de cloture avec des costumes (4 personnes sur scène)"></p><p>C’est ainsi que je me suis retrouvé à improviser une conférence “ennuyeuse” sur les nombres premiers face à une salle remplit de développeuses et développeurs ! Ce moment restera dans ma mémoire comme le jour où j’ai fait de l’impro dans le grand auditorium de la cité des congrès 😎.</p><p>Au final, cette keynote a surpris de part son originalité et a rempli le contrat initial : faire rire après 2 jours de conférences techniques intenses !</p><iframe width="560" height="315" src="https://www.youtube.com/embed/ZehrVDBW3SQ?si=P2qko39GRk8FRYhT" title="keynote de cloture sur youtube" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>keynote de cloture sur youtube<h1 id="Pour-aller-plus-loin"><a href="#Pour-aller-plus-loin" class="headerlink" title="Pour aller plus loin"></a>Pour aller plus loin</h1><p>Cet article est issue d’une série d’articles sur l’organisation du DevFest Nantes. Vous pouvez retrouver les autres articles ici :</p><ul><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-1/">Père JF, raconte nous le DevFest Nantes - La genèse</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-2/">Père JF, raconte nous le DevFest Nantes - Les premières éditions, l’ère Epitech</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-3/">Père JF, raconte nous le DevFest Nantes - Un nouveau lieu, de nouveaux challenges</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-4/">Père JF, raconte nous le DevFest Nantes - Un évènement grandissant</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-5/">Père JF, raconte nous le DevFest Nantes - On pousse les murs encore une fois !</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-6/">Père JF, raconte nous le DevFest Nantes - L’équipe et sa taille</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-7/">Père JF, raconte nous le DevFest Nantes - Les 10 ans</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-8/">Père JF, raconte nous le DevFest Nantes - L’après</a></li></ul>]]></content>
<summary type="html"><p><img src="/assets/2024-01-DevFestNantesPereCastor/devfest-nantes.jpeg" alt="Image du Logo du DevFest Nantes"></p>
<p>Les 10 ans !!! Enfin</summary>
<category term="Event" scheme="http://jef.binomed.fr/categories/Event/"/>
<category term="Devfest" scheme="http://jef.binomed.fr/tags/Devfest/"/>
<category term="Organisation" scheme="http://jef.binomed.fr/tags/Organisation/"/>
</entry>
<entry>
<title>Père JF, raconte nous le DevFest Nantes - L'équipe et sa taille</title>
<link href="http://jef.binomed.fr/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-6/"/>
<id>http://jef.binomed.fr/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-6/</id>
<published>2024-01-31T13:06:00.000Z</published>
<updated>2024-01-31T16:34:39.739Z</updated>
<content type="html"><![CDATA[<p><img src="/assets/2024-01-DevFestNantesPereCastor/devfest-nantes.jpeg" alt="Image du Logo du DevFest Nantes"></p><p>Avant de parler de la fameuse 10ième édition, je pense qu’il est temps de parler de l’équipe et de son fonctionnement.</p><p>En effet, le DevFest Nantes est un événement prenant, riche en stress, en potentielles prises de tête. Il est donc essentiel d’avoir une bonne entente entre les membres de l’association pour s’assurer du bon fonctionnement de cette dernière.</p><p>De plus, le DevFest n’est organisé que par des bénévoles, donc même si nos sociétés respectives nous aident en fermant les yeux sur du temps que nous pouvons passer à l’organisation de ce dernier, nous passons toutes et tous beaucoup de temps à organiser cet événement sur notre temps perso ! Ça peut aller de l’équivalent de 300h sur son temps perso pour certains à minimum ~100h à 150h pour la plupart des membres de l’équipe. La cohésion et la bonne entente sont donc de mise ! L’équipe se qualifie même très souvent de “bande de potes”. Nous avons vu grandir les enfants des uns, nous connaissons les conjoint·es des autres, bref, la team du DevFest, c’est comme une famille !</p><p><img src="/assets/2024-01-DevFestNantesPereCastor/team-devfest.jpeg" alt="L'équipe actuelle organisatrice du DevFest Nantes (10 personnes devant la cité des congrès)"></p><p>Quelques éléments majeurs doivent être implicitement respectés par les membres de l’association :</p><ul><li>L’engagement communautaire ! Un des éléments clés et fondateur du succès du DevFest vis-à-vis de cette équipe est son profond engagement pour la communauté ! Aucun·e d’entre nous ne faisons ça par intérêt personnel mais bel et bien pour le bien de la communauté et des 3 personas cités précédemment.</li><li>Chacun·e s’implique comme il·elle peut ! En effet, le bénévolat est une chose où chacun·e donne à la hauteur du temps qu’il·elle a de disponible. L’organisation d’un événement, c’est 1% d’organisateur·rice, 99% de consomateurs·rices. Ce qui sous-entend que la grosse majorité des personnes consomment et que l’énergie dépensée par les bénévoles est-elle assez forte. Il faut donc respecter la capacité de chacun·e à s’engager de manière individuelle.</li></ul><p>Très rapidement, nous avons mis en place un mécanisme de cooptation où une personne ne peut rentrer dans l’association que si un membre de l’association est à l’origine de son entrée. Les autres membres ont toutes et tous le droit de véto. De plus, une période d’essai d’un an est ainsi appliquée pour valider l’entrée ou non de la personne dans l’association.</p><p>De cette manière-là, en l’espace de 10 éditions, l’association a vu doucement mais surement grossir son équipe pour atteindre une taille maximum de 10 organisateurs·rices à l’année avec dans toute l’histoire du DevFest seulement 2 fois où une personne n’est pas allé au bout de la période d’essais.</p><p>Cependant le DevFest a réfléchi son organisation. Seule une petite poignée de personnes pilotent au quotidien le Devfest. Mais le DevFest, c’est aussi des prestataires de confiances qui nous connaissent et nous suivent depuis le début, des bénévoles engagés qui sont notre force de frappe le jour J. A titre d’exemple en 2023, le DevFest Nantes, c’était 120 bénévoles ! Puis enfin pour nous soulager, nous avons récement mis en place des “bénévoles ++” sortes de référent·es d’équipes qui nous aide à gérer les bénévoles le jour J.</p><p>Cette gestion nous permet de piloter efficacement le DevFest en sachant exactement ce qu’il faut reproduire et quand, pour chacun des différents postes du DevFest. De cettte manière, chacun·e gère son retro planning et s’appuyera ensuite sur nos prestataires de confiances pour mener à bien les projets, où des référents bénévoles le jour J que nous aurons breefé sur la marche à suivre. Je compare souvent l’organisation du DevFest à un mariage dans le sens où on met plus d’un an à organiser les détails et se passe le jour J ce qu’il doit se passer ! Notre rôle le jour J se résume finalement à gérer les urgences en lien avec nos zones de responsabilités.</p><p>Dernier élément mis en place il y quelque temps : nous doublons systématiquement le savoir par poste. En effet, nous devons accepter que nous ne sommes que des humains et nous réfléchissons en terme de “Bus Factor” : si un bus vous écrase ce matin, qu’advient-il de ce que vous aviez en charge ? Avec ceci en tête, nous faisons en sorte de ne pas mettre en risque un aspect du DevFest. Cependant, au delà de la théorie, il faut quand même avouer que c’est un aspect que nous avons le plus de mal à appliquer au quotidien. Heureusement, l’équipe au global fait en sorte de se rapeller à nous-même régulièrement ces règles !</p><h1 id="Pour-aller-plus-loin"><a href="#Pour-aller-plus-loin" class="headerlink" title="Pour aller plus loin"></a>Pour aller plus loin</h1><p>Cet article est issue d’une série d’articles sur l’organisation du DevFest Nantes. Vous pouvez retrouver les autres articles ici :</p><ul><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-1/">Père JF, raconte nous le DevFest Nantes - La genèse</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-2/">Père JF, raconte nous le DevFest Nantes - Les premières éditions, l’ère Epitech</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-3/">Père JF, raconte nous le DevFest Nantes - Un nouveau lieu, de nouveaux challenges</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-4/">Père JF, raconte nous le DevFest Nantes - Un évènement grandissant</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-5/">Père JF, raconte nous le DevFest Nantes - On pousse les murs encore une fois !</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-6/">Père JF, raconte nous le DevFest Nantes - L’équipe et sa taille</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-7/">Père JF, raconte nous le DevFest Nantes - Les 10 ans</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-8/">Père JF, raconte nous le DevFest Nantes - L’après</a></li></ul>]]></content>
<summary type="html"><p><img src="/assets/2024-01-DevFestNantesPereCastor/devfest-nantes.jpeg" alt="Image du Logo du DevFest Nantes"></p>
<p>Avant de parler de l</summary>
<category term="Event" scheme="http://jef.binomed.fr/categories/Event/"/>
<category term="Devfest" scheme="http://jef.binomed.fr/tags/Devfest/"/>
<category term="Organisation" scheme="http://jef.binomed.fr/tags/Organisation/"/>
</entry>
<entry>
<title>Père JF, raconte nous le DevFest Nantes - On pousse les murs encore une fois !</title>
<link href="http://jef.binomed.fr/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-5/"/>
<id>http://jef.binomed.fr/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-5/</id>
<published>2024-01-31T13:05:00.000Z</published>
<updated>2024-01-31T16:34:39.723Z</updated>
<content type="html"><![CDATA[<p><img src="/assets/2024-01-DevFestNantesPereCastor/devfest-nantes.jpeg" alt="Image du Logo du DevFest Nantes"></p><p>L’expension du DevFest continuait et nous avons décidé de prendre cette fois ci, le haut et le bas de la cité.</p><h1 id="Edition-2018-l’espace"><a href="#Edition-2018-l’espace" class="headerlink" title="Edition 2018 - l’espace !"></a>Edition 2018 - l’espace !</h1><p>Une nouvelle édition : de nouveaux challenges ! Et oui, pourquoi finalement s’arrêter en si bon chemin :). Sky is the limit !</p><iframe width="560" height="315" src="https://www.youtube.com/embed/gNchOTSmdI4?si=jtur8vwUqL8sOOBn" title="Vidéo d'intro du DevFest 2018 sur le thème de l'espace" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>Vidéo d'intro du DevFest 2018 sur le thème de l'espace<p>Pour cette année, nous avons pris quasiment tous les espaces de la cité, un seul espace reste encore à conquérir : le grand auditorium (mais ça sera plus tard 😉). Ce nouveau gain d’espace nous a permis d’accueillir toujours plus de monde et de continuer notre expension pour atteindre les 1800 parcitipant·es par jour !!</p><p><img src="/assets/2024-01-DevFestNantesPereCastor/vitro.jpeg" alt="Photo du DevFest de l'extérieur (devant de la cité des congrès avec une fille d'attente de personnes)"></p><p>Ces nouveaux espaces nous ont permis de réfléchir à des meilleures capacités d’accueil de nos participant·es et en amenant pour la première fois des vraies animations dans l’espace de la cité :</p><ul><li>Mise en place d’une expo de maquettes de l’espace</li><li>Animation planetarium</li></ul><p><img src="/assets/2024-01-DevFestNantesPereCastor/curiosity.jpeg" alt="Photo de la maquette de curiosity"></p><p>Toujours dans un souci d’innovation et de contribution, l’équipe du DevFest Nantes en la personne de Benjamin Petetot démarra un projet qui est à présent très diffusé dans le monde des conférences Françaises et dans certaines conférences européennes à savoir l’outil de CFP (Call For Paper) Conference Hall : <a href="https://conference-hall.io/">https://conference-hall.io/</a></p><p>Dans les blagues de cette édition, on pourra quand même noter la chose suivante : Notre billetterie avec impression d’étiquette qui marchait si bien. Tout aurait très bien marché en 2018 encore si une personne malveillente n’avait pas décidé de venir avec un brouilleur WIFI mettant à plat notre solution et provoquant l’inverse de ce qui était prévu 🥷… Nous n’aurons jamais su qui fut à l’origine de cette attaque ni pourquoi mais cela nous a amenés à revoir notre système pour opter pour une version 100% filaire.</p><p>Dans les nouveautés que nous avons mis en place sur cette édition, des MC (Master of Ceremony). Cette idée, nous l’avions prise au Web2Day (événement Nantais très connu) et trouvions que c’était une très bonne chose pour nos speakers d’avoir une personne chargée de les présenter et leur indiquer quand ils·elles doivent démarrer leurs conférences. Les MCs sont toujours très appréciés de nos speakers car ils·elles sont comme des sortes de “nounous” qui prennent soin de nos speakers en les rassurant juste avant la conférence 🫂.</p><p>Cette année, Audrey Le Mercier intégra les effectifs de l’association ce qui nous amenait à 8 organisateurs·rices.</p><p><img src="/assets/2024-01-DevFestNantesPereCastor/team-2018.jpeg" alt="L'équipe du DevFest Nantes en 2018 (8 personnes devant la cité des congrès)"></p><p>Enfin cette édition restera marquante dans l’esprit de beaucoup de monde grâce à la keynote de fermeture où nous avons présenté un burger quiz qui étonna tout autant qu’elle était drôle.</p><p><img src="/assets/2024-01-DevFestNantesPereCastor/burger-quizz.jpeg" alt="Image du burger quizz fait pendant le devfest nantes (écran avec les scores du burger quiz, 2 équipes de 3 par tables et un présentateur en tablier)"></p><p>L’année 2018 en chiffres donne donc la chose suivante :</p><ul><li>1800 participant·es par jour (2500 visiteurs uniques)</li><li>70 sessions</li><li>81 speakers</li></ul><h1 id="Edition-2019-l’annee-du-rock"><a href="#Edition-2019-l’annee-du-rock" class="headerlink" title="Edition 2019 : l’année du rock !"></a>Edition 2019 : l’année du rock !</h1><p>En soi l’année 2019 fut une année de stabilisation où nous avons pris une décision majeure -> Arrêter de grossir. En effet, le DevFest 2019 représente 2000 participant·es par jour ce qui est déjà largement suffisant pour le lieu. En effet, même si un lieu comme la cité des congrès pourrait accueillir plus de monde, augmenter veut dire dégrader l’expérience participant·es</p><iframe width="560" height="315" src="https://www.youtube.com/embed/o7BzM-yAz_M?si=0utIesAmIaww9D4o" title="Vidéo d'intro du DevFest 2019 sur le thème du rock" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>Vidéo d'intro du DevFest 2019 sur le thème du rock<p>Cette édition restera surtout dans les esprits pour son thème graphique qui fut l’un des plus beaux thèmes que l’on a l’occasion d’avoir au DevFest</p><p><img src="/assets/2024-01-DevFestNantesPereCastor/devfesthero.jpeg" alt="photo de la scène principale du Devfest Nantes 2019 (sur l'écran on voit un jeu type guitar hero et sur scène on voit un pupitre avec un ordi)"></p><p>En 2019, nous avons aussi fait intégrer dans l’association Marie Cauchy. Et de son côté Benjamin Petetot se retirait de l’organisation sur l’année pour laisser place à Audrey aux commandes de la partie Design.</p><p><img src="/assets/2024-01-DevFestNantesPereCastor/team-2019.jpeg" alt="Equipe du DevFest en 2019 (9 personnes devant un van)"></p><p>Dans les éléments “étranges” de cette année, nous avons été ciblés par des bots twitter de canaux pornographiques et le Hashtag #devfestnantes fut complètement inondé pendant 3h de tweets pornographiques. Nous avons donc dû signaler un à un tous les tweets et mettre nos bénévoles dans le coup pour nous aider à faire disparaître ces tweets malveillants. Cette anecdote restera encore dans nos esprits car, c’était la première fois que nous faisions face à une telle attaque et nous ne savions pas comment réagir 😅. Heureusement, dans l’après-midi, le problème était réglé et cet épisode n’était qu’une histoire de plus dans la vie du DevFest Nantes.</p><h1 id="2020-Ah-bah-non-2021-Episode-COVID"><a href="#2020-Ah-bah-non-2021-Episode-COVID" class="headerlink" title="2020 ! Ah bah non 2021 : Episode COVID"></a>2020 ! Ah bah non 2021 : Episode COVID</h1><p>Cette édition fut l’occasion de faire les choses de manières un peu différemment car nous avions dû annuler(reporter) notre édition 2020 pour cause de COVID. Au moment de la vente des packs, nous ne savions pas que le Covid allait arriver et donc nous avions récupéré tous les packs de sponsoring. Nous avons ainsi proposé à nos sponsors 2 choses : nous laisser la trésorerie au cas où en 2021 nous pourrions organiser l’événement / se faire rembourser.</p><iframe width="560" height="315" src="https://www.youtube.com/embed/0TFyOHrIxoM?si=Z7WiLfU8kMZdzHBX" title="Vidéo d'intro du DevFest 2021 sur le thème du street art" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>Vidéo d'intro du DevFest 2021 sur le thème du street art<p>Par chance, une grande partie de nos partenaires nous ont suivis et ont décidés de nous laisser la trésorerie ce qui nous a permis de garder nos prestataires et aussi d’assurer la continuité de l’événement. Nous avons même réussi à gérer l’événement avec le seulement les sponsors restants.</p><p><img src="/assets/2024-01-DevFestNantesPereCastor/devfest21.jpeg" alt="Devant de la cité des congrès avec la fresque de cette édition collée sur les vitres"></p><p>2021 fut aussi l’occasion pour nous d’accueillir 2 petits nouveaux : Aline Deschamps et Arthur Maury mais aussi de dire au revoir à Xavier Marc pour qui cette édition fut la dernière en tant qu’organisateur puisqu’il retournait dans sa terre natale : la Bretagne.</p><p><img src="/assets/2024-01-DevFestNantesPereCastor/team-2020.jpeg" alt="L'équipe du DevFest 2021 (11 personnes sur la photo devant la cité des congrès)"></p><p>Bon, j’ai menti… En 2019 on avait dit qu’on arrêtait de grandir et du coup en 2021, on a quand même tenté de passer à 2100 par jour… Ce chiffre nous a montré cependant que pour l’espace que nous occupions (surtout au niveau des salles), 2100 personnes c’était trop !</p><h1 id="Pour-aller-plus-loin"><a href="#Pour-aller-plus-loin" class="headerlink" title="Pour aller plus loin"></a>Pour aller plus loin</h1><p>Cet article est issue d’une série d’articles sur l’organisation du DevFest Nantes. Vous pouvez retrouver les autres articles ici :</p><ul><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-1/">Père JF, raconte nous le DevFest Nantes - La genèse</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-2/">Père JF, raconte nous le DevFest Nantes - Les premières éditions, l’ère Epitech</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-3/">Père JF, raconte nous le DevFest Nantes - Un nouveau lieu, de nouveaux challenges</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-4/">Père JF, raconte nous le DevFest Nantes - Un évènement grandissant</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-5/">Père JF, raconte nous le DevFest Nantes - On pousse les murs encore une fois !</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-6/">Père JF, raconte nous le DevFest Nantes - L’équipe et sa taille</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-7/">Père JF, raconte nous le DevFest Nantes - Les 10 ans</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-8/">Père JF, raconte nous le DevFest Nantes - L’après</a></li></ul>]]></content>
<summary type="html"><p><img src="/assets/2024-01-DevFestNantesPereCastor/devfest-nantes.jpeg" alt="Image du Logo du DevFest Nantes"></p>
<p>L’expension du DevFe</summary>
<category term="Event" scheme="http://jef.binomed.fr/categories/Event/"/>
<category term="Devfest" scheme="http://jef.binomed.fr/tags/Devfest/"/>
<category term="Organisation" scheme="http://jef.binomed.fr/tags/Organisation/"/>
</entry>
<entry>
<title>Père JF, raconte nous le DevFest Nantes - Un évènement grandissant</title>
<link href="http://jef.binomed.fr/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-4/"/>
<id>http://jef.binomed.fr/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-4/</id>
<published>2024-01-31T13:04:00.000Z</published>
<updated>2024-01-31T16:34:39.722Z</updated>
<content type="html"><![CDATA[<p><img src="/assets/2024-01-DevFestNantesPereCastor/devfest-nantes.jpeg" alt="Image du Logo du DevFest Nantes"></p><p>Voyant le succès toujours gradissant du DevFest, nous avons réfléchi à notre offre, avec plusieurs questions majeures :</p><ul><li>Passons-nous à 2 jours ?</li><li>Comment amener les participant·es à passer plus de temps sur les stands ?</li><li>Comment clôturer 2 jours de festival ?</li><li>Comment encourager des speakers “débutant·es” à venir prendre la parole ?</li><li>Organisons-nous un temps dédié pour les speakers si on est sur 2 jours ?</li><li>Aurons-nous assez de speakers pour 2 jours ?</li></ul><p>Face à tous ces défis, nous avons décidé de réfléchir les choses, une fois encore, à destination des 3 personas. Voici les conclusions :</p><h1 id="Edition-2016-passage-a-2-jours"><a href="#Edition-2016-passage-a-2-jours" class="headerlink" title="Edition 2016 - passage à 2 jours"></a>Edition 2016 - passage à 2 jours</h1><p>Le passage à 2 jours nous paraissait être une suite logique. Cependant, il était aussi clair que jamais nous n’irions au-delà de 2 jours. En effet, après avoir participé à un grand nombre d’événements, une conférence de 3 jours est toujours très fatiguante et génère ainsi des effets de public qui déserte les salles de conférences le 3ème jour. Cela allait à l’encontre de notre personas Speaker !</p><p>Qui dit passage sur 2 jours, dit aussi augmentation de nos frais. Nous avons dû de nouveau revoir nos tarifs, tout en gardant en tête l’aspect accessible de l’événement. Nous avons donc mis en place des tarifs 1 JOUR dont le prix était proche de ce qu’on avait l’année précédente. Tout cela en préservant un tarif défiant toute concurrence pour les étudiants :</p><ul><li>Pass 1J : 40€</li><li>Pass 2J : 70€</li><li>Pass étudiant 1J : 10€</li></ul><p>Les packs sponsors ont eux aussi augmenté en conséquence, nous permettant de garder notre équilibre.</p><p>Grâce à tout cela, nous allions pouvoir accueillir beaucoup plus de monde sur l’événement puisque nous ciblions 700 participant·es par jour !</p><p>Ce passage à 2 jours nous a aussi amenés à réfléchir à la manière dont devait s’articuler l’événement en termes de temps forts, des messages à passer, d’ambiance à donner…</p><p>C’est ainsi qu’en 2016 nous avons décidé d’avoir une keynote d’ouverture qui serait une conférence ayant pour vocation de faire “rêver” mais ayant toujours une connexion avec le numérique. La conférence de clôture, quant à elle, doit être un temps de détente et de rigolade. Les keynotes d’ouverture et de fermeture sont les 2 seules conférences sur lesquelles, nous nous autorisons à “payer” l’intervenant·e. Le sujet de leur conférence pouvant être leur travail au quotidien, nous ne souhaitions pas exploiter des artistes à travers le DevFest.</p><p>C’est de cette manière que nous avons accueilli notre premier artiste pour la Keynote d’ouverture : un beat boxer nommé Ezra qui utilise un gant connecté pour jouer avec ses loops</p><iframe width="560" height="315" src="https://www.youtube.com/embed/cqwmytyukRg?si=eiiDruUB8ICxGU5U" title="Vidéo de la keynote d'ouverture avec Ezra en artiste" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>Vidéo de la keynote d'ouverture avec Ezra en artiste<p>Pour la Keynote de fermeture, nous l’avons créé de toutes pièces avec la participation de <a href="https://www.commitstrip.com/fr/">commitstrip</a> sur le thème d’une parodie de “qui veut gagner des millions”.</p><p><img src="/assets/2024-01-DevFestNantesPereCastor/question-pour-dev.jpeg" alt="Keynote de fermeture "qui veut gagner ma carrière de CTO" (on voit 2 personnes sur scènes avec l'écran de qui veut gagner des millions en version dev)"></p><h2 id="Plus-de-temps-plus-de-conferences"><a href="#Plus-de-temps-plus-de-conferences" class="headerlink" title="Plus de temps = plus de conférences ?"></a>Plus de temps = plus de conférences ?</h2><p>Une de nos craintes fut de trouver 2 fois plus de speakers si nous passions à 2 jours. Pour résoudre ce problème, nous avons analysé nos personas “Sponsors” et “Speakers”.</p><p>Nous n’avons pas doublé le nombre de speakers mais nous avons mieux réparti nos sessions et ajouté un format : les quickies (des talks de 20 min).</p><p>L’enjeu était simple : moins de conférences en fin de journée et plus de quickies, les participant·es étant plus généralement fatigués sur ce créneau. Ce qui a pour résultat : 40 conférences (vs 30 en 2015), 16 quickies et 8 codelabs (vs 6 en 2015).</p><p>Nous avons aussi augmenté très fortement le temps de pause entre chaque session afin de laisser le temps aux participant·es de passer sur les stands. Nous avons également rallongé le temps de pause le midi. De cette manière, nous permettons aux participant·es et aux sponsors d’avoir un vrai temps d’échange ! Le thème ayant été amené sur l’année précédente, les sponsors commençaient à jouer le jeu et le DevFest Nantes commençait à ressembler à ce que nous voulions ! Le thème des Legos avait aussi beaucoup contribué à inspirer nos sponsors ^^.</p><p><img src="/assets/2024-01-DevFestNantesPereCastor/theme-lego.jpeg" alt="photo d'un casque minifig lego d'un sponsor"></p><h2 id="Accueillir-les-speakers-debutant·es"><a href="#Accueillir-les-speakers-debutant·es" class="headerlink" title="Accueillir les speakers débutant·es"></a>Accueillir les speakers débutant·es</h2><p>Il nous tenait à coeur d’offrir des possibilités à des speakers débutant·es de pouvoir venir prendre la parole sur notre événement. Cependant, comment s’y prendre pour ne pas prendre de risque sur le programme ? C’est ainsi que nous avons eu l’idée de mettre en place des conférences de 20 min en fin de journée. Plutôt que de faire comme les autres conférences de France où les quickies sont sur la pause déjeuner (et où globalement, il y a assez peu d’audience), nous souhaitions préserver nos speakers en leur offrant un vrai public tout en leur permettant de s’essayer sur des belles salles !</p><p>Depuis cette édition, nous sélectionnons chaque année des “nouveaux·elles” speakers sur ces créneaux.</p><h2 id="Quelques-axes-a-ameliorer"><a href="#Quelques-axes-a-ameliorer" class="headerlink" title="Quelques axes à améliorer"></a>Quelques axes à améliorer</h2><p>Suite à cette édition, nous avons mis en place un de nos rituels : le <strong>REX</strong>. Chaque année après le DevFest, nous nous réunissons pour passer en revue tous les aspects positifs et négatifs du DevFest pour nous améliorer. Voici par exemple quelques-unes des “difficultés” rencontrés cette édition.</p><p>Avoir des speakers internationaux, c’est bien, mais il faut leur donner l’occasion de consommer du contenu anglais. Sinon, ils s’ennuient et ne reviendront pas… Ainsi à la suite de cette édition, nous avons pris la décision d’essayer d’avoir sur tout l’événement 30% du contenu en anglais. Si cela n’était pas possible, la solution était de regrouper tous les speakers anglophones sur une seule journée.</p><p>Lors ce DevFest, j’avais beaucoup de missions à assurer : La réalisation du compte à rebours, l’écriture de la keynote de clôture, gérer les animations GDG et les taches organisationnelles de l’événement mais également présenter un talk technique en tant que speaker. La charge de travail était trop intense et mon corps m’a lâché le deuxième jour de l’événement. Résultat : impossible pour moi d’animer la keynote de clôture. Heureusement, Etienne de Commitstrip avait suivi de près l’écriture du talk et a pu reprendre à la dernière minute ma place. Cet incident nous a fait prendre une décision radicale : Interdiction, pour les organisateurs, de présenter des talks au DevFest Nantes (hors keynote d’ouverture et de fermeture).</p><p>Dernière amélioration que nous avons dû apporter. Depuis les débuts du DevFest, afin d’attirer les sponsors, nous garantissions pour les niveaux “Platinium” la possibilité d’avoir un talk dédié. Or, vu notre le nombre grandissant de sponsors platiniums, cela devenait un problème en termes de qualité pour notre programme. Les talks proposés ne correspondaient pas toujours à nos attentes et à nos critères de sélection. Nous avons dû malheureusement refuser des talks dans notre CFP pourtant de bonnes qualités au profit de certains talks sponsors moins qualitatifs. La décision fut simple : supprimer cet avantage pour nos sponsors platiniums. Cette décision fut plus ou moins bien acceptée mais elle était importante pour la qualité de l’événement.</p><p>Au final, l’édition 2016 se résume en ces chiffres :</p><ul><li>700 participant·es par jour (environ 1000 personnes uniques)</li><li>68 conférences et codelabs</li><li>70 speakers</li></ul><h1 id="Edition-2017-on-s’agrandit-encore"><a href="#Edition-2017-on-s’agrandit-encore" class="headerlink" title="Edition 2017 - on s’agrandit encore"></a>Edition 2017 - on s’agrandit encore</h1><p>2017 fut pour nous l’occasion de voir encore plus grand en abandonnant le haut de la cité des congrès pour tester le bas et ainsi avoir à notre disposition des salles encore plus grandes dont une très belle salle de 800 places !</p><p><img src="/assets/2024-01-DevFestNantesPereCastor/salle-titan.jpeg" alt="salle titan : 800 places (on voit une grande salle avec sur scène un logo géant du devfest et 2 écrans latéraux)"></p><p>Le plus gros challenge pour cette édition fut de composer avec de nouveaux espaces, réfléchir réellement aux flux de circulation dans la cité des congrès afin de faire en sorte que les sponsors ayant payé le plus puissent être le mieux placés.</p><p>Nous avons aussi profité de ces nouveaux espaces pour plus que doubler la fréquentation du DevFest en un an passant de 700 participant·es par jour à 1600 par jour !</p><p>C’est donc aussi depuis cette édition que nous avons eue à mettre en oeuvre une armée de bénévoles le jour J car il nous devenait impossible de gérer correctement l’événement juste avec nous et quelques bénévoles.</p><p>Cette édition sous le thème des super héros fut aussi la première édition où les sponsors jouaient vraiment quasiment tous le jeu du thème actant vraiment ce que nous avions voulu mettre en oeuvre quelques années au préalable. Les allées étaient pleines de jeux en tout genre ou de projets fils rouges codés par les équipes de dev pendant l’année pour le DevFest ! On sentait la fierté des équipes invitées à venir présenter leur projet, leurs idées. Les sponsors redoublaient maintenant d’idées pour attirer les participant·es sur leurs stands à coups de jeux concours, de goodies, …quelque chose de grand était en route 😊. Nous avons quand même dû apprendre de nos erreurs et commencer à la suite de cette édition à se décider de “valider” les animations de nos sponsors car certaines animations étaient vraiment trop bruyantes et faisaient passer le DevFest pour une fête foraine 🎡.</p><p>2017 fut aussi la première édition où Annabelle Koster (l’actuelle présidente de l’association) rejoignait officiellement l’équipe et ouvrait ainsi la voie vers une féminisation de notre équipe.</p><p><img src="/assets/2024-01-DevFestNantesPereCastor/photo-team-2017.jpeg" alt="photo de l'équipe du GDG en 2017 (7 personnes représentées)"></p><h2 id="Toujours-plus-d’experimentations"><a href="#Toujours-plus-d’experimentations" class="headerlink" title="Toujours plus d’expérimentations"></a>Toujours plus d’expérimentations</h2><p>Lors de cette année, nous avons aussi mis en place pour la première fois en france un nouveau système de notation de conférence qui était le résultat d’une discussion avec <a href="https://twitter.com/hsablonniere">Hubert Sablonnière</a> et qui avait démarré un repo github allant dans ce sens. C’est ainsi qu’en 2017, nous lancions le “bingo des feedbacks”. Une grille d’émotions permettant aux participant·es de s’exprimer sur le ressenti d’une conférence et ne se limitant pas à une simple note dont on ne sait jamais quelle a été l’indicateur noté : le·a speaker ? la technicité du talk ? les slides ? … Notre système mettait donc en avant des sentiments qu’un·e speaker pourrait comprendre et lui donnerait l’occasion de progresser ou se conforter. On pouvait ainsi retrouver les ressentis suivants :</p><ul><li>Pas assez technique</li><li>Trop technique</li><li>Trop mou</li><li>Bon</li><li>Pas clair</li><li>Hyper Intéressant</li><li>J’ai appris quelque chose</li><li>J’aime les démos</li><li>Ça manque de démos</li><li>Ça manque de code</li><li>Trop de code</li><li>Trop de démos</li></ul><p><img src="/assets/2024-01-DevFestNantesPereCastor/bingo-feedback.jpeg" alt="Image du bingo de feedback (une grille avec des gommettes collées dessus)"></p><p>Pour rendre ce dernier engageant, nous avions pris la décision de le mettre bien en avant et de le rendre ludique en demandant aux participant·es de coller des gommettes. Nous récupérions par la suite ces fiches que nous envoyons aux speakers.</p><p>Depuis, <a href="https://twitter.com/HugoGresse">Hugo Gresse</a> à Montpellier a créé une version numérique de ce concept qui est maintenant répandu à travers les conférences. Merci à lui pour son produit opensource et gratuit qu’est openfeedback : <a href="https://openfeedback.io/">https://openfeedback.io/</a>. De notre côté, nous utilisons bien maintenant openfeedback mais pas avec les sentiments proposés par défaut.</p><p>Encore une expérimentation, Aymeric qui est fan d’électronnique nous concocta une version de billetterie qui imprimait sur place l’étiquette du participant·e. Cette nouveauté technologique nous a permis d’offrir une expérience à nos particpant·es enrichie avec un temps d’attente maitrisé et une entrée dans les lieues accélérée.</p><p>Pour terminer dans nos expérimentations, nous avons aussi eu l’énorme chance d’importer un concept américain sur le sol Français : “Speechless Live!”. En effet, à chaque google I/O, il y avait cette session complètement déjantée où des speakers de google s’affrontaient à coups de slides toujours plus débiles les uns que les autres mais avec un tel aplond et assurance que cette session était toujours un régal. Nous avons donc pris contact avec la société organisatrice de ce show et avons obtenu les droits d’organiser des Speechless en France ! Depuis ce temps, le GDG Nantes organise régulièrement des sessions qui sont toujours un moment de fun et de convivialité.</p><p><img src="/assets/2024-01-DevFestNantesPereCastor/speechless.jpeg" alt="Photo du speechless avec un speaker et une poule sur l'écran"></p><p>Enfin pour finir, avec ce thème, nous avons aussi commencé à prendre de l’indépendance sur la création de nos réalisations graphiques et nous avons commencé à travailler avec <a href="https://luizalaffitte.com/">Luiza Laffite</a> qui sera en charge de toutes les productions graphiques depuis cette édition. Cela nous aussi donné l’occasion de créer des vidéos d’intros sortant tout droit de notre imaginaire et ceci rendait vraiment bien grâce au travail de Luiza et de <a href="https://vimeo.com/nicolascocaud">Nicolas Cocaud</a> son motion designer référent.</p><iframe width="560" height="315" src="https://www.youtube.com/embed/yLjfNCMARCw?si=e5lxv6tL2FpLNoD6" title="Premier motion design du DevFest Nantes sur le thème des super héros" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>Premier motion design du DevFest Nantes sur le thème des super héros<p>Pour terminer, le DevFest 2017 c’était :</p><ul><li>1600 participant·es par jour (2200 uniques environ)</li><li>64 conférences et codelabs</li><li>85 speakers</li></ul><h1 id="Pour-aller-plus-loin"><a href="#Pour-aller-plus-loin" class="headerlink" title="Pour aller plus loin"></a>Pour aller plus loin</h1><p>Cet article est issue d’une série d’articles sur l’organisation du DevFest Nantes. Vous pouvez retrouver les autres articles ici :</p><ul><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-1/">Père JF, raconte nous le DevFest Nantes - La genèse</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-2/">Père JF, raconte nous le DevFest Nantes - Les premières éditions, l’ère Epitech</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-3/">Père JF, raconte nous le DevFest Nantes - Un nouveau lieu, de nouveaux challenges</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-4/">Père JF, raconte nous le DevFest Nantes - Un évènement grandissant</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-5/">Père JF, raconte nous le DevFest Nantes - On pousse les murs encore une fois !</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-6/">Père JF, raconte nous le DevFest Nantes - L’équipe et sa taille</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-7/">Père JF, raconte nous le DevFest Nantes - Les 10 ans</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-8/">Père JF, raconte nous le DevFest Nantes - L’après</a></li></ul>]]></content>
<summary type="html"><p><img src="/assets/2024-01-DevFestNantesPereCastor/devfest-nantes.jpeg" alt="Image du Logo du DevFest Nantes"></p>
<p>Voyant le succès tou</summary>
<category term="Event" scheme="http://jef.binomed.fr/categories/Event/"/>
<category term="Devfest" scheme="http://jef.binomed.fr/tags/Devfest/"/>
<category term="Organisation" scheme="http://jef.binomed.fr/tags/Organisation/"/>
</entry>
<entry>
<title>Père JF, raconte nous le DevFest Nantes - Un nouveau lieu, de nouveaux challenges</title>
<link href="http://jef.binomed.fr/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-3/"/>
<id>http://jef.binomed.fr/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-3/</id>
<published>2024-01-31T13:03:00.000Z</published>
<updated>2024-01-31T16:34:39.739Z</updated>
<content type="html"><![CDATA[<p><img src="/assets/2024-01-DevFestNantesPereCastor/devfest-nantes.jpeg" alt="Image du Logo du DevFest Nantes"></p><p>Cette idée folle était née dès le premier DevFest “et si un jour le DevFest allait à la cité des congrès ?”. Cet espace dédié aux grands salons, lieux de prestige Nantais… Ce dernier nous paraissait inaccessible pour les premières éditions. Pourtant, nous avons décidé de tenter l’aventure. On sentait que le DevFest Nantes avait du potentiel et on voulait y croire.</p><blockquote><p>“C’est par ce qu’ils ne savaient pas que c’était impossible qu’ils l’ont fait”.</p></blockquote><p>Cette citation de Mark Twain nous collait bien, étant juste animés par notre motivation, nous avons décidé de relever le défi !</p><p><img src="/assets/2024-01-DevFestNantesPereCastor/foyer-haut.jpeg" alt="Espace occupé à la cité des congrès de Nantes pour le DevFest Nantes 2014"></p><h1 id="Edition-2014-L’arrivee-a-la-cite-des-congres"><a href="#Edition-2014-L’arrivee-a-la-cite-des-congres" class="headerlink" title="Edition 2014 : L’arrivée à la cité des congrès"></a>Edition 2014 : L’arrivée à la cité des congrès</h1><p>Cette édition fut un énorme challenge notamment car la location de la cité des congrès nous a amené des problématiques que nous n’avions pas anticipées. Un espace des congrès est un espace qui doit gagner de l’argent sur de l’événementiel et donc TOUT est une option payante ! Voici quelques-unes des surprises que nous avons eu la première année :</p><ul><li>L’électricité par stand est payante</li><li>Internet par prise ethernet est payant</li><li>Qui dit grandes salles, dit, prestataires lumière, son, vidéo</li><li>La sécurité est payante</li><li>Le traiteur doit être un de ceux choisis par la cité</li><li>L’installation doit se faire la veille mais nécessite de payer le lieu la veille aussi</li><li>La gestion du “projet” DevFest au sens de la cité est une prestation à payer</li><li>Une scénographie doit être pensée pour ne pas avoir des scènes vides</li><li>Plein d’autres petits détails !</li></ul><p><img src="/assets/2024-01-DevFestNantesPereCastor/scene-450.jpeg" alt="Photo de la scène principale du DevFest Nantes 2014 (un écran et un présentoir)"></p><p>Cette première édition à la Cité nous a permis de grossir en sécurisant notre budget. Nous avions en effet un enjeu de taille : Comment conserver notre modèle et nos envies avec un budget qui fait x2,5…?</p><p>Une fois encore, notre réflexion sur les 3 personas nous a permis de trouver un équilibre :</p><ul><li>500 participant·es avec une place à 30€. Introduction d’une place étudiant·es à 10€</li><li>Augmentation du nombre de sponsors (plus d’espace pour eux)</li><li>Suppression des codelabs sur cette édition</li></ul><p>Cet événement fut un de ceux dont le budget était le plus serré car nous avons clôturé l’événement tout juste avec ce qui était prévu.</p><p>Autres expérimentations en 2014 : Proposer des animations développées par nos soins. Nous avons décidé d’amener un concept venant du Google I/O lui-même, à savoir le compte à rebours dynamique. En attendant que la conférence de lancement du Google I/O démarre, l’écran affiche un compte à rebours géant travaillé graphiquement, qui passe avec de la musique et parfois des animations pour faire participer le public en attendant.</p><p>Nous avons donc créé notre premier compte à rebours pour occuper les participants avant le démarrage officiel du DevFest Nantes.</p><p>C’est le premier d’une longue série, puisque depuis 2014, je cherche tous les ans des animations de compte à rebours de plus en plus fun ! Du dessin en live, en passant par Guitar Hero ou encore des quiz sur les films, … Peut-être le sujet d’un futur article ?</p><p><img src="/assets/2024-01-DevFestNantesPereCastor/countdown-donkey.png" alt="Compte à rebours de 2014 (image de la tour de bretagne avec un compte à rebours)"></p><p>Cette intro en musique fut aussi l’occasion d’amener un autre de nos rituels qui reste d’actualité encore maintenant, la dernière chanson du compte à rebours doit être “AC/DC - Thunderstruck”. Cette chanson marquera pour toujours nos esprits au moment du lancement de nos keynotes. Chaque année, quand cette chanson démarre, les membres de l’équipe présent pour l’animation de la keynote ont toutes et tous ce moment de frisson et l’adrénaline qui monte 🤘(et on adore ça !)</p><iframe width="560" height="315" src="https://www.youtube.com/embed/v2AC41dglnM?si=fmpbw_poqvdKz74x" title="Vidéo du clip AC/DC Thunderstruck" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe><p>Toujours dans notre souhait d’aller plus loin dans l’expérience DevFest, l’édition 2014 fut l’occasion de faire notre première “After Party” directement dans l’espace de la cité.</p><p>2014 fut aussi l’année où nous avons accueilli Pierrick Guyard comme nouveau membre de l’association pour nous aider sur l’événement.</p><p>Pour finir, 2014 fut pour nous l’occasion de travailler de manière étroite avec un de nos meilleurs partenaires : <a href="https://mstream.fr/">MStream</a>, notre régisseur vidéo. Ce dernier aura été notre meilleur conseiller à travers toutes ces années pour nous aider dans nos réflexions et propositions d’améliorations sur notre événement. Ils nous ont aidés à asseoir une légitimité sur la place des conférences tech car nous étions une des rares à cette époque à avoir des vidéos aussi qualitatives à partager post-event.</p><h1 id="Edition-2015-theme-retro-gaming"><a href="#Edition-2015-theme-retro-gaming" class="headerlink" title="Edition 2015 - thème retro gaming"></a>Edition 2015 - thème retro gaming</h1><p>L’édition 2015 était pour nous un équivalent de notre 2ème édition. En effet, le changement de lieu avait eu un tel impact que cette édition 2015 avait été synonyme de confirmation de ce coup d’essai qu’était le passage à la cité des congrès.</p><p>Cependant, 2015 fut aussi, une fois n’est pas coutume, l’occasion d’expérimenter un concept qui changea profondément le DevFest Nantes et eu un impact majeur dans l’écosystème des conférences en France. Je veux bien sûr parler du “Thème” !</p><p>Nous devions nous faire à l’idée que le travail allait grandissant de DevFest en DevFest et que nous n’aurions plus le temps de gérer nous-même l’aspect animations qui garantissait du “fun” pour les participant·es. Graphiquement, l’espace de la cité des Congrès n’était pas assez mis en avant car les stands ressemblaient “juste” à une foire d’exposition sans âme… C’est ainsi que nous est venue cette idée ! Et si nos sponsors s’occupaient de faire l’animation, la déco à notre place ? Si on les guidait à travers cette idée de se rendre plus attractifs ? De fédérer leurs équipes pendant l’année grâce à un projet commun ? De leur donner l’occasion de se mettre en avant le jour J pour que les participant·es aient envie de passer sur leur stand ? C’est ainsi que nous avons décidé d’imposer ce concept de thème.</p><p>La première année, nous avons choisi le thème du retro-gaming. Seuls les sociétés dans lesquels les membres de l’équipe travaillaient avaient joué le jeu. Mais cela avait suffit à montrer aux autres sponsors la marche à suivre pour les années futures !</p><iframe width="560" height="315" src="https://www.youtube.com/embed/-3TrNRHOpJQ?si=k7QOB-IwxBCbdpDr" title="Vidéo d'intro du DevFest 2015 dans un style rétrogaming" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe><p>L’édition 2015 nous a aussi permis de remettre en place les codelabs qui étaient chers à nos participant·es.</p><p>Ainsi le DevFest 2015 se résume en ces chiffres :</p><ul><li>600 participant·es</li><li>40 speakers</li><li>30 conférences + 6 codelabs</li><li>6 organisateurs</li></ul><h1 id="Pour-aller-plus-loin"><a href="#Pour-aller-plus-loin" class="headerlink" title="Pour aller plus loin"></a>Pour aller plus loin</h1><p>Cet article est issue d’une série d’articles sur l’organisation du DevFest Nantes. Vous pouvez retrouver les autres articles ici :</p><ul><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-1/">Père JF, raconte nous le DevFest Nantes - La genèse</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-2/">Père JF, raconte nous le DevFest Nantes - Les premières éditions, l’ère Epitech</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-3/">Père JF, raconte nous le DevFest Nantes - Un nouveau lieu, de nouveaux challenges</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-4/">Père JF, raconte nous le DevFest Nantes - Un évènement grandissant</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-5/">Père JF, raconte nous le DevFest Nantes - On pousse les murs encore une fois !</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-6/">Père JF, raconte nous le DevFest Nantes - L’équipe et sa taille</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-7/">Père JF, raconte nous le DevFest Nantes - Les 10 ans</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-8/">Père JF, raconte nous le DevFest Nantes - L’après</a></li></ul>]]></content>
<summary type="html"><p><img src="/assets/2024-01-DevFestNantesPereCastor/devfest-nantes.jpeg" alt="Image du Logo du DevFest Nantes"></p>
<p>Cette idée folle éta</summary>
<category term="Event" scheme="http://jef.binomed.fr/categories/Event/"/>
<category term="Devfest" scheme="http://jef.binomed.fr/tags/Devfest/"/>
<category term="Organisation" scheme="http://jef.binomed.fr/tags/Organisation/"/>
</entry>
<entry>
<title>Père JF, raconte nous le DevFest Nantes - Les premières éditions, l'ère Epitech</title>
<link href="http://jef.binomed.fr/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-2/"/>
<id>http://jef.binomed.fr/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-2/</id>
<published>2024-01-31T13:02:00.000Z</published>
<updated>2024-01-31T16:34:39.748Z</updated>
<content type="html"><![CDATA[<p><img src="/assets/2024-01-DevFestNantesPereCastor/devfest-nantes.jpeg" alt="Image du Logo du DevFest Nantes"></p><p>Maintenant que le contexte est posé, regardons un peu comment cela s’est déroulé à Nantes.</p><h1 id="1ere-edition-2012"><a href="#1ere-edition-2012" class="headerlink" title="1ère édition (2012)"></a>1ère édition (2012)</h1><p>Été 2012, Google communique avec tous les GDG pour leur expliquer qu’un nouveau programme va voir le jour au sein de la communauté : Les DevFest ! Cette marque offerte aux GDG a pour seul critère de faire un événement sur une journée !</p><p>Côté équipe Nantaise, nous échangeons sur la pertinence d’organiser un tel évent à Nantes plutôt qu’à Paris. Nous étions convaincus qu’un DevFest à Nantes ne marcherait pas 😅.</p><p>Après des discussions avec l’équipe du GDG Paris qui n’ont pas abouti, nous prenons la décision suivante le 14 Septembre 2012 : Nous allons organiser notre premier DevFest Nantes le 9 novembre 2012 dans les locaux d’Epitech !</p><p>L’arrivée de Xavier Marc dans l’équipe apporta un dynamisme qui nous motiva à organiser cette édition.</p><p>En l’espace d’un mois, nous avons :</p><ul><li>Créé un événement de toute pièce sur une journée</li><li>Invité des speakers</li><li>Créé des démos pour rendre l’événement attractif</li><li>Créé une Keynote d’ouverture</li><li>Transformé une école en évent communautaire</li><li>Trouvé des goodies à offrir</li></ul><p>Voici en quelques chiffres la première édition du DevFest :</p><ul><li>15 conférences sur 3 tracks</li><li>3 codelabs sur 1 tracks dédié</li><li>150 participant·es</li><li>17 speakers</li><li>4 organisateurs</li><li>10 bénévoles</li></ul><p><img src="/assets/2024-01-DevFestNantesPereCastor/progarmme-2012.png" alt="Programme du 1er Devfest Nantes"></p><p>Voici les bases de ce qui fera la renommée de l’événement :</p><ul><li>Des speakers de qualité. Nous avions la chance d’avoir, dès cette première édition, des speakers de renoms dont des core contributeurs de technos très sollicités en 2012.</li><li>Une expérience participant·es aux petits oignons : de quoi bien manger, des noms de salles en rapport avec Nantes (qu’on retrouve encore aujourd’hui), des sponsors locaux et des goodies !</li></ul><p><img src="/assets/2024-01-DevFestNantesPereCastor/amphi-2012.jpeg" alt="Photo de l'amphithéatre du premier Devfest"></p><p>L’équipe découvrait alors les joies de l’organisation d’un tel événement comme :</p><ul><li>Découper 150 badges à la main</li><li>Remplir 150 sacs de goodies</li><li>Faire dormir des bénévoles dans Epitech pour s’assurer de ne pas se faire voler du matériel 😅</li><li>Passer des câbles réseau à travers les faux plafonds pour amener du réseaux dans toutes les salles</li><li>Cette première édition était le reflet de nos envies. Il fallait maintenant se poser la question de l’avenir de cet événement.</li></ul><h1 id="2eme-edition-2013"><a href="#2eme-edition-2013" class="headerlink" title="2ème édition - 2013"></a>2ème édition - 2013</h1><p>Notre objectif était clair : reproduire l’expérience du Google I/O à Nantes ! Ou en tout cas, s’en rapprocher le plus possible. Mais comment faire ? Nous n’avons ni le budget de Google, ni la possibilité de faire venir les rock stars du monde technologique.</p><p><img src="/assets/2024-01-DevFestNantesPereCastor/epitech-2013.jpeg" alt="Photo d'Epitech avec la bannière du DevFest Nantes 2013"></p><p>C’est ainsi qu’en 2013, l’équipe a commencé à penser le DevFest comme un produit informatique avec 3 personas principaux en tête.</p><ul><li>Les participant·es :<ul><li>Proposer du contenu de qualité, rechercher de speakers de qualités</li><li>Proposer un événement accessible financièrement : 20€ la journée</li><li>Proposer des animations pour s’amuser et discuter autour de technologies</li><li>Bien manger, car en France, on aime ça ^^</li><li>Goodies offerts</li></ul></li><li>Les sponsors :<ul><li>Donner de la visibilité sur le site internet, sur les réseaux sociaux et sur place.</li><li>Permettre de rencontrer des potentiels candidat·es</li><li>Offrir des places pour venir à l’événement</li><li>Leur réserver un talk pour venir parler et présenter un sujet</li></ul></li><li>Les speakers :<ul><li>Mettre à disposition des outils et un accompagnement leur donnant envie de revenir et parler du DevFest en bien.</li><li>Leur organiser une soirée dédiée à la fin de l’événement</li><li>Prendre en charge leurs transports pour certains</li><li>Prendre de photographies de qualités pour qu’ils puissent les utiliser</li><li>Mettre à disposition une salle dédiée</li><li>Capter les conférences et mettre à disposition les vidéos afin de pouvoir capitaliser sur leur conférence</li></ul></li></ul><p>Pour chaque persona, l’enjeu est simple : fidéliser et faire parler de l’événement. De cette manière, nous pouvions construire le DevFest de l’année suivante sur le succès de l’année précédente en sachant exactement quoi reprendre et quoi améliorer.</p><p>Ainsi en 2013, nous décidons d’aller plus loin sur différents aspects :</p><ul><li>Créer une keynote avec un contenu “inspirant”</li><li>Proposer des animations réalisées par le GDG : pas 1 démo, pas 2 démos mais bien 3 démos : vélo connecté en réalité augmentée, * Portal gun à base de WebRTC, consultation de programme à base d’interface gestuelle avec leapmotion</li><li>Filmer les sessions pour les mettre à disposition après l’événement (au final, la qualité n’était au rendez-vous que pour deux salles : <a href="https://www.youtube.com/watch?v=4Dyrsy2_-vU&list=PLuZ_sYdawLiWFwTzvodAr6VU0zBs7Hz2z&ab_channel=Epitech">Playlist DevFest Nantes 2013</a>)</li><li>Avoir un photographe dédié pour pouvoir communiquer proprement</li><li>Ouvrir un track 100% non technique</li><li>Offrir des t-shirts en plus d’autres goodies</li><li>Augmenter le nombre de conférences et de participant·es :<ul><li>23 conférences / 3 codelabs</li><li>350 participant·es</li><li>30 speakers</li><li>5 organisateurs</li></ul></li></ul><p>Cette deuxième édition nous aura permis de confirmer l’intérêt d’un tel événement à Nantes. Le public a répondu présent une nouvelle fois mais nous faisons face à une première limite : le lieu ! Bien que très bien situé, Epitech n’a plus la capacité d’accueil suffisante en accord avec nos objectifs de croissance.</p><p>Cette édition nous aura aussi permis de faire grandir l’équipe avec un des mécanismes qui sera un des piliers de notre association pour les années futures : la cooptation ! (Nous reviendrons sur cet aspect plus tard 😉). Ainsi en 2013, Aymeric Fouchault (directeur Epitech Nantes) rejoint l’équipe.</p><h1 id="Pour-aller-plus-loin"><a href="#Pour-aller-plus-loin" class="headerlink" title="Pour aller plus loin"></a>Pour aller plus loin</h1><p>Cet article est issue d’une série d’articles sur l’organisation du DevFest Nantes. Vous pouvez retrouver les autres articles ici :</p><ul><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-1/">Père JF, raconte nous le DevFest Nantes - La genèse</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-2/">Père JF, raconte nous le DevFest Nantes - Les premières éditions, l’ère Epitech</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-3/">Père JF, raconte nous le DevFest Nantes - Un nouveau lieu, de nouveaux challenges</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-4/">Père JF, raconte nous le DevFest Nantes - Un évènement grandissant</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-5/">Père JF, raconte nous le DevFest Nantes - On pousse les murs encore une fois !</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-6/">Père JF, raconte nous le DevFest Nantes - L’équipe et sa taille</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-7/">Père JF, raconte nous le DevFest Nantes - Les 10 ans</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-8/">Père JF, raconte nous le DevFest Nantes - L’après</a></li></ul>]]></content>
<summary type="html"><p><img src="/assets/2024-01-DevFestNantesPereCastor/devfest-nantes.jpeg" alt="Image du Logo du DevFest Nantes"></p>
<p>Maintenant que le co</summary>
<category term="Event" scheme="http://jef.binomed.fr/categories/Event/"/>
<category term="Devfest" scheme="http://jef.binomed.fr/tags/Devfest/"/>
<category term="Organisation" scheme="http://jef.binomed.fr/tags/Organisation/"/>
</entry>
<entry>
<title>Père JF, raconte nous le DevFest Nantes - La genèse</title>
<link href="http://jef.binomed.fr/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-1/"/>
<id>http://jef.binomed.fr/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-1/</id>
<published>2024-01-31T13:01:00.000Z</published>
<updated>2024-01-31T16:34:39.743Z</updated>
<content type="html"><![CDATA[<p><img src="/assets/2024-01-DevFestNantesPereCastor/devfest-nantes.jpeg" alt="Image du Logo du DevFest Nantes"></p><p>La scène commence au bord d’un feu de cheminée qui est en fait une vidéo enregistrée dans les bureaux de SFEIR Nantes. Une équipe de dev se rassemble et commence à parler de communautés, de conférences. Jean-François arrive dans la discussion quand il leur dit : “Tiens ça me fait penser à une histoire, est-ce que je vous ai déjà raconté l’histoire du DevFest Nantes ?”. Tout le monde s’assied autour du fauteuil sur lequel Jean-François est assis et l’histoire commence…</p><p><img src="/assets/2024-01-DevFestNantesPereCastor/pere-castor.webp" alt="image de père castor"></p><h1 id="La-genese"><a href="#La-genese" class="headerlink" title="La genèse"></a>La genèse</h1><p>Avant le DevFest fut les DevFest…? Bon, en fait c’est un peu plus compliqué que ça…</p><h2 id="GTUG-community"><a href="#GTUG-community" class="headerlink" title="GTUG community"></a>GTUG community</h2><p>Dans les années 2000, le monde des communautés de développement informatique voit le jour un peu partout dans le monde sous le nom de “Users Groups”. Dans cette même période, Google s’impose avec une vision nouvelle des technologies qu’il partage avec le monde entier. En France, à cette époque-là, nous connaissons surtout les JUG (Java User Group) qui sont actifs.</p><p>Ainsi naissent à travers le monde un petit nombre de GTUG (Google Technologie User Group). Le Nantes GTUG en fera partie et fut créé en janvier 2011 par Julien Landuré, Benjamin Petetot et moi-même Jean-François Garreau.</p><p><img src="/assets/2024-01-DevFestNantesPereCastor/gtug-nantes.png" alt="Logo du Nantes GTUG en 2011 (Lettres G T U G en couleur avec des éléments de Nantes)"></p><p>Toujours en 2011, Google organise comme depuis quelques années déjà son <a href="https://io.google/">Google I/O</a>. Ce dernier se déroule uniquement à San-Francisco. Pour ceux qui n’avaient pas la chance d’assister en live à cet événement, ils avaient le droit à un GDD (Google Developer Day). En 2011, il y en a eu un en Amérique du sud, un à Moscou et un à Berlin en novembre. Et pour finir, si, vous n’aviez ni I/O, ni GDD, Google organisait dans les pays où il était implanté des “DevFest” ! Ainsi en 2011, Google Paris organisait le premier DevFest Français.</p><h2 id="GDG-Community"><a href="#GDG-Community" class="headerlink" title="GDG Community"></a>GDG Community</h2><p>Comme toute chose, il y a un “mais” ! Les ingénieurs de Google se retrouvaient à parcourir le monde à donner des conférences, beaucoup d’énergie et de budgets étaient dépensés par les équipes de Google… Résultat, cette façon de faire ne dura qu’un an.</p><p>En 2012, Google se rendant compte que sa communauté de GTUG grossit (environ une centaine à travers le monde) décide de reprendre un peu les choses en main et ainsi de déléguer ! Google décide donc plusieurs choses :</p><ul><li>Renommer les GTUG en GDG (Google Developer Group)</li><li>Offrir la marque DevFest aux GDG avec pour seule contrainte, l’événement doit durer plus de 8h et se passer entre Septembre et Novembre.</li></ul><h3 id="Instant-Anecdote…"><a href="#Instant-Anecdote…" class="headerlink" title="Instant Anecdote…"></a><em>Instant Anecdote…</em></h3><p><em>Le passage de GTUG a GDG a provoqué en 2012 un énorme tollé dans le monde communautaire en place. En effet, quelques GTUG s’étaient créés depuis 3, 4 années et le changement de nom “imposé” par Google fut considéré comme appropriation d’une communauté par un acteur majeur et un gros risque quand à l’autonomie dont jouissaient les organisateurs·rices !</em></p><p><em>La communauté en place fut donc scindée en 2. D’un côté, ceux qui voyaient d’un bon oeil ce changement car cela montrait que Google voulait s’investir plus dans l’animation d’une communauté de développement. De plus, Google avait à cette époque-là fait le tour des GTUGs pour expliquer leur vision et la liberté qui serait laissée. De l’autre, ceux qui considéraient qu’iels allaient perdre leur autonomie et qu’iels allaient se faire imposer une manière de penser !</em></p><p><em>C’est ainsi qu’une partie des organisateurs·rices allemands se retirèrent du programme à cette époque-là.</em></p><h3 id="…Reprise-de-l’histoire"><a href="#…Reprise-de-l’histoire" class="headerlink" title="…Reprise de l’histoire"></a><em>…Reprise de l’histoire</em></h3><p>C’est donc de cette manière que cette marque est arrivée entre les mains de différents GDG à travers le monde en cette fin d’année 2012.</p><p><img src="/assets/2024-01-DevFestNantesPereCastor/logo-gdg-nantes.png" alt="Logo du GDG Nantes en 2012 (Chevron coloré à gauche puis GDG Nantes écrit avec en fond des éléments de Nantes comme l'éléphant)"></p><h1 id="Pour-aller-plus-loin"><a href="#Pour-aller-plus-loin" class="headerlink" title="Pour aller plus loin"></a>Pour aller plus loin</h1><p>Cet article est issue d’une série d’articles sur l’organisation du DevFest Nantes. Vous pouvez retrouver les autres articles ici :</p><ul><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-1/">Père JF, raconte nous le DevFest Nantes - La genèse</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-2/">Père JF, raconte nous le DevFest Nantes - Les premières éditions, l’ère Epitech</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-3/">Père JF, raconte nous le DevFest Nantes - Un nouveau lieu, de nouveaux challenges</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-4/">Père JF, raconte nous le DevFest Nantes - Un évènement grandissant</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-5/">Père JF, raconte nous le DevFest Nantes - On pousse les murs encore une fois !</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-6/">Père JF, raconte nous le DevFest Nantes - L’équipe et sa taille</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-7/">Père JF, raconte nous le DevFest Nantes - Les 10 ans</a></li><li><a href="/2024/01/31/2024-01-31-pere-jf-raconte-nous-le-devfest-part-8/">Père JF, raconte nous le DevFest Nantes - L’après</a></li></ul>]]></content>
<summary type="html"><p><img src="/assets/2024-01-DevFestNantesPereCastor/devfest-nantes.jpeg" alt="Image du Logo du DevFest Nantes"></p>
<p>La scène commence au</summary>
<category term="Event" scheme="http://jef.binomed.fr/categories/Event/"/>
<category term="Devfest" scheme="http://jef.binomed.fr/tags/Devfest/"/>
<category term="Organisation" scheme="http://jef.binomed.fr/tags/Organisation/"/>
</entry>
<entry>
<title>I organized a Project Fugu hackathon</title>
<link href="http://jef.binomed.fr/2020/11/03/2020-11-03-I-organize-a-fugu-hackathon/"/>
<id>http://jef.binomed.fr/2020/11/03/2020-11-03-I-organize-a-fugu-hackathon/</id>
<published>2020-11-03T13:21:01.000Z</published>
<updated>2024-01-26T15:18:47.707Z</updated>
<content type="html"><![CDATA[<p>A month ago, I organized (with a colleague of mine, <a href="https://twitter.com/geromegrignon">Gérôme Grignon</a>) a Hackathon based on the Project Fugu “Shape Detection API”.</p><p><img src="/assets/2020-10-push-the-limit/push-the-limit.png" alt="morty"></p><h1 id="TLDR"><a href="#TLDR" class="headerlink" title="TLDR;"></a>TLDR;</h1><p>The shape detection api is very easy web API to use but it has lots of limitations according to the platform you have. If you are curious about what we done, you can read this article 😇.</p><h1 id="Push-the-limit"><a href="#Push-the-limit" class="headerlink" title="Push the limit"></a>Push the limit</h1><p>As you can imagine, we wanted to create a fun Hackathon, a hackthon where the fun and the creativity were more important than the social impact of the result 😅.</p><p>The main idea of this hackathon was to play with the <a href="https://web.dev/shape-detection/">Shape Detection API</a>. </p><blockquote><p>TLDR; This API lets you find your shapes in images. The shape is restricted by the web platform, you could recognize a face, a barcode or some text (not implemented yet)</p></blockquote><h1 id="Under-the-hood"><a href="#Under-the-hood" class="headerlink" title="Under the hood"></a>Under the hood</h1><p>This API is interesting because with very few lines of codes you have something that gives you the position of shapes detected in an ImageBitmapSource. An image is nice, but a canvas is better 😉. Indeed, a canvas can generate data that the detector could interpret.</p><p>Here is for example how you can detect a face: </p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> faceDetector = <span class="keyword">new</span> <span class="title class_">FaceDetector</span>({</span><br><span class="line"> <span class="comment">// (Optional) Hint to try and limit the amount of detected faces</span></span><br><span class="line"> <span class="comment">// on the scene to this maximum number.</span></span><br><span class="line"> <span class="attr">maxDetectedFaces</span>: <span class="number">5</span>,</span><br><span class="line"> <span class="comment">// (Optional) Hint to try and prioritize speed over accuracy</span></span><br><span class="line"> <span class="comment">// by, e.g., operating on a reduced scale or looking for large features.</span></span><br><span class="line"> <span class="attr">fastMode</span>: <span class="literal">false</span></span><br><span class="line">});</span><br><span class="line"><span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">const</span> faces = <span class="keyword">await</span> faceDetector.<span class="title function_">detect</span>(image);</span><br><span class="line"> faces.<span class="title function_">forEach</span>(<span class="function"><span class="params">face</span> =></span> <span class="variable language_">console</span>.<span class="title function_">log</span>(face));</span><br><span class="line">} <span class="keyword">catch</span> (e) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">error</span>(<span class="string">'Face detection failed:'</span>, e);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>The object face returned is an array with all positions of the detected faces in the image or canvas.</p><p>Here is the result of face detection: </p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">[</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">boudingBox</span>: <span class="comment">// DOMRectReadOnly / The position and size of the face</span></span><br><span class="line"> {</span><br><span class="line"> <span class="attr">bottom</span>: xxx.<span class="property">xxx</span> <span class="comment">// absolute bottom position in the image</span></span><br><span class="line"> <span class="attr">height</span>: xxx.<span class="property">xxx</span> <span class="comment">// absolute height in the image</span></span><br><span class="line"> <span class="attr">left</span>: xxx.<span class="property">xxx</span> <span class="comment">// absolute left position in the image</span></span><br><span class="line"> <span class="attr">right</span>: xxx.<span class="property">xxx</span> <span class="comment">// absolute right position in the image</span></span><br><span class="line"> <span class="attr">top</span>: xxx.<span class="property">xxx</span> <span class="comment">// absolute top position in the image</span></span><br><span class="line"> <span class="attr">width</span>: xxx.<span class="property">xxx</span> <span class="comment">// absolute width in the image</span></span><br><span class="line"> <span class="attr">x</span>: xxx.<span class="property">xxx</span> <span class="comment">// absolute left position in the image</span></span><br><span class="line"> <span class="attr">y</span>: xxx.<span class="property">xxx</span> <span class="comment">// absolute top position in the image</span></span><br><span class="line"> },</span><br><span class="line"> <span class="attr">landmarks</span>: <span class="comment">// Some points to position eyes, mouth and nose</span></span><br><span class="line"> [</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">type</span>: <span class="string">'eye'</span>, <span class="comment">// could be also 'mouth' or 'nose'</span></span><br><span class="line"> <span class="attr">locations</span>: <span class="comment">// Array of point that you use to draw the part of face</span></span><br><span class="line"> [</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">x</span>: xxx.<span class="property">xxx</span> <span class="comment">// left position of element in the image</span></span><br><span class="line"> <span class="attr">y</span>: xxx.<span class="property">xxx</span> <span class="comment">// right position of element in the image</span></span><br><span class="line"> }</span><br><span class="line"> , ...</span><br><span class="line"> ]</span><br><span class="line"> }</span><br><span class="line"> ,...</span><br><span class="line"> ]</span><br><span class="line"> }</span><br><span class="line"> ,...</span><br><span class="line">]</span><br></pre></td></tr></table></figure><p>Here is the result of barcode detection: </p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">[</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">boudingBox</span>: <span class="comment">// DOMRectReadOnly / The position and size of the face</span></span><br><span class="line"> {</span><br><span class="line"> <span class="attr">bottom</span>: xxx.<span class="property">xxx</span> <span class="comment">// absolute bottom position in the image</span></span><br><span class="line"> <span class="attr">height</span>: xxx.<span class="property">xxx</span> <span class="comment">// absolute height in the image</span></span><br><span class="line"> <span class="attr">left</span>: xxx.<span class="property">xxx</span> <span class="comment">// absolute left position in the image</span></span><br><span class="line"> <span class="attr">right</span>: xxx.<span class="property">xxx</span> <span class="comment">// absolute right position in the image</span></span><br><span class="line"> <span class="attr">top</span>: xxx.<span class="property">xxx</span> <span class="comment">// absolute top position in the image</span></span><br><span class="line"> <span class="attr">width</span>: xxx.<span class="property">xxx</span> <span class="comment">// absolute width in the image</span></span><br><span class="line"> <span class="attr">x</span>: xxx.<span class="property">xxx</span> <span class="comment">// absolute left position in the image</span></span><br><span class="line"> <span class="attr">y</span>: xxx.<span class="property">xxx</span> <span class="comment">// absolute top position in the image</span></span><br><span class="line"> },</span><br><span class="line"> <span class="attr">format</span>: <span class="string">'qr_code'</span>, <span class="comment">// Could be 'aztec', 'code_128', 'code_39', 'code_93', 'codabar',</span></span><br><span class="line"> <span class="comment">// 'data_matrix', 'ean_13', 'ean_8', 'itf', 'pdf417', 'qr_code', 'upc_a', 'upc_e'</span></span><br><span class="line"> <span class="attr">rawValue</span>: <span class="string">'text in the code'</span>, <span class="comment">// the value of the barcode</span></span><br><span class="line"> <span class="attr">cornerPoints</span>: <span class="comment">// Position of corner points of barcode</span></span><br><span class="line"> [</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">x</span>: xxx.<span class="property">xxx</span> <span class="comment">// left position of element in the image</span></span><br><span class="line"> <span class="attr">y</span>: xxx.<span class="property">xxx</span> <span class="comment">// right position of element in the image</span></span><br><span class="line"> }</span><br><span class="line"> , ...</span><br><span class="line"> ]</span><br><span class="line"> }</span><br><span class="line"> ,...</span><br><span class="line">]</span><br></pre></td></tr></table></figure><p>For the moment, the TextDetector is not yet universally available 😅.</p><h1 id="Some-Helpers-for-particpants"><a href="#Some-Helpers-for-particpants" class="headerlink" title="Some Helpers for particpants"></a>Some Helpers for particpants</h1><p>Even if the API is really easy to use, combine it to video, canvas, a performant mechanism is a synonym of lots of boilerplate. Indeed, when you want to play with the camera, you need to follow those steps to be sure to not block the UI thread.</p><ol><li>Start the camera with the correct parameters</li><li>Inject the stream to an HTML <code>video</code> element (and keep a copy somewhere)</li><li>Start to play with <code>requestAnimationFrame</code> to be sure to optimize the rendering.</li><li>On each frame, draw the video on your canvas</li><li>Start a detection in asynchronous mode (the result will arrive in another frame)</li><li>If a result of detection is present, then you can draw something</li></ol><p>Each of those steps has specific code and it could be lots of code for just detection and drawing something 😅. That’s why we create some helpers that you could use too if you want for your projects.</p><h2 id="Detector-js"><a href="#Detector-js" class="headerlink" title="Detector.js"></a>Detector.js</h2><p>This file wraps and exposes methods to detect things on the image</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Detector</span> {</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Will create the write dectector object according to the type.</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> type: the type of detector to use -> see constant TYPES to use the correct types</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> options: each detector should work with specifics options that could override. Check the desire constant. Note that text detector don't need options</span></span><br><span class="line"><span class="comment"> **/</span></span><br><span class="line"> <span class="title function_">constructor</span>(<span class="params">type, options</span>) {}</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Where the magic happens ;)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> image: the source image or canvas where we want to detect something</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@returns</span> a Promise with the result of the detection</span></span><br><span class="line"><span class="comment"> **/</span></span><br><span class="line"> <span class="title function_">detect</span>(<span class="params">image</span>) {}</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* Helper function to check if the browser supports the feature</span></span><br><span class="line"><span class="comment">* <span class="doctag">@return</span> true or false according to your browser support</span></span><br><span class="line"><span class="comment">**/</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">isAvailable</span>(<span class="params">type</span>) {}</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* The managed types</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">const</span> <span class="variable constant_">TYPES</span> = {</span><br><span class="line"> <span class="attr">face</span>: <span class="string">"face"</span>,</span><br><span class="line"> <span class="attr">text</span>: <span class="string">"text"</span>,</span><br><span class="line"> <span class="attr">barcode</span>: <span class="string">"barcode"</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* The options of face</span></span><br><span class="line"><span class="comment">* You can override the number of detected faces</span></span><br><span class="line"><span class="comment">* FastMode is to use when you want to have a quick result. The result will be less precise if fastMode is set to true</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">const</span> <span class="variable constant_">OPTIONS_FACE</span> = {</span><br><span class="line"> <span class="attr">maxDetectedFaces</span>: <span class="number">1</span>,</span><br><span class="line"> <span class="attr">fastMode</span>: <span class="literal">false</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* The list if BARCODE format to check. You can reduce this list if you want to speed detection process</span></span><br><span class="line"><span class="comment">**/</span></span><br><span class="line"><span class="keyword">const</span> <span class="variable constant_">OPTIONS_BARCODE</span> = {</span><br><span class="line"> <span class="attr">formats</span>: [</span><br><span class="line"> <span class="string">"aztec"</span>,</span><br><span class="line"> <span class="string">"code_128"</span>,</span><br><span class="line"> <span class="string">"code_39"</span>,</span><br><span class="line"> <span class="string">"code_93"</span>,</span><br><span class="line"> <span class="string">"codabar"</span>,</span><br><span class="line"> <span class="string">"data_matrix"</span>,</span><br><span class="line"> <span class="string">"ean_13"</span>,</span><br><span class="line"> <span class="string">"ean_8"</span>,</span><br><span class="line"> <span class="string">"itf"</span>,</span><br><span class="line"> <span class="string">"pdf417"</span>,</span><br><span class="line"> <span class="string">"qr_code"</span>,</span><br><span class="line"> <span class="string">"upc_a"</span>,</span><br><span class="line"> <span class="string">"upc_e"</span></span><br><span class="line"> ]</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>The source code is available here <a href="https://gist.github.com/jefBinomed/031d77184db58768468e81738108ee7b#file-detector-js">Gist Detector</a></p><h2 id="UserMedia-js"><a href="#UserMedia-js" class="headerlink" title="UserMedia.js"></a>UserMedia.js</h2><p>UserMedia is the API used to request camera input. We create this class :</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">UserMediaHelper</span> {</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> canvas: the dom element corresponding to canvas</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> video: the dom element corresponding to video</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> videoArea: the dom element around the canvas (to fix the size of the output video)</span></span><br><span class="line"><span class="comment"> **/</span></span><br><span class="line"> <span class="title function_">constructor</span>(<span class="params">canvas, video, videoArea</span>) {}</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Method to call to receive the results of the detection</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> type: the type of detector to use -> See the Helper Detector to have more information</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> options: the options passed to the previous detector. If you want to use TextDetector, please give "null" as value</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> callback: the callback function that will receive the results of the detection. The function should be like (detectedObject)=>{}</span></span><br><span class="line"><span class="comment"> **/</span></span><br><span class="line"> <span class="title function_">addDetectorCallbak</span>(<span class="params">type, options, callback</span>) {}</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Method to call if you want to draw something on the canvas based on the detection</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> callBackFunction: the method call at every frame to draw on the canvas. The function should be like (context, video, canvas) => {}</span></span><br><span class="line"><span class="comment"> **/</span></span><br><span class="line"> <span class="title function_">addCallbackDraw</span>(<span class="params">callBackFunction</span>) {}</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * This method should be called to stop the process of captation and detection</span></span><br><span class="line"><span class="comment"> **/</span></span><br><span class="line"> <span class="title function_">stop</span>(<span class="params"></span>) {}</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Call this method to start the captation and detection process</span></span><br><span class="line"><span class="comment"> **/</span></span><br><span class="line"> <span class="keyword">async</span> <span class="title function_">getUserMedia</span>(<span class="params"></span>) {}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>The source code is available here <a href="https://gist.github.com/jefBinomed/031d77184db58768468e81738108ee7b#file-usermedia-js">Gist usermedia</a></p><p>As you can see, the UserMedia helper uses a detector.</p><p>The Helper provides also a method to stop your camera in the right way 😎.</p><h2 id="CodeSandBox"><a href="#CodeSandBox" class="headerlink" title="CodeSandBox"></a>CodeSandBox</h2><p>To continue helping attendees, we created a codesandbox sample to clone so that attendees could start directly. Here is the result of the codesandbox.</p><p>To be sure that it works with your configuration, check if you have enabled the flags.</p><ul><li>Demo: <a href="https://bg3o4.csb.app/">Pickle Rick Me</a></li><li>CodeSandBox to clone: <a href="https://codesandbox.io/s/young-tree-bg3o4">CodeSandBox Push The Limit</a></li></ul><h3 id="Demos-writen"><a href="#Demos-writen" class="headerlink" title="Demos writen"></a>Demos writen</h3><p>Here is the list of demos people write :)</p><ul><li>Face Face Revolution (a dance dance revolution but with your face): <a href="https://codesandbox.io/s/z3opl">https://codesandbox.io/s/z3opl</a></li><li>Draw a pickle rick if a barcode is found (android only): <a href="https://codesandbox.io/s/891md">https://codesandbox.io/s/891md</a></li><li>Like Pickel Rick but with a random character of smash ultimate game: <a href="https://codesandbox.io/s/festive-wiles-f9hbs?file=/src/index.js">https://codesandbox.io/s/festive-wiles-f9hbs?file=/src/index.js</a></li><li>Draw a “joker” smile and if you open your mouth show a grumpycat (only specifics version, see bugs and restrictions after): <a href="https://codesandbox.io/s/elegant-mayer-up20v">https://codesandbox.io/s/elegant-mayer-up20v</a></li><li>A Game where a Pickle Rick have to eat pickle coming from the top of the screen: <a href="https://codesandbox.io/s/young-flower-s0162">https://codesandbox.io/s/young-flower-s0162</a></li></ul><h3 id="Special-Trick-for-image"><a href="#Special-Trick-for-image" class="headerlink" title="Special Trick for image"></a>Special Trick for image</h3><p>During the preparation of this hackathon, we faced a problem with the pickle image and the drawing of the pickle on the canvas. Indeed, I didn’t know it before but Canvas is also sensible to CORS origin security when you draw an image! </p><p>To understand that, you have to understand that codesandbox doesn’t host the image in the same origin of your page. The resource is redirected under the hood to a bucket S3. To bypass this problem, we simply write a script that downloads the image from the amazon server, extracts the data, converts them to base64 and injects this base64 data as the source to the original <code>img</code>. In that way, you can continue to draw images on canvas without facing any CORS problem 🥳</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">function</span> <span class="title function_">prepareImage</span>(<span class="params">img</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function">(<span class="params">resolve</span>) =></span> {</span><br><span class="line"> <span class="title function_">fetch</span>(img.<span class="property">src</span>)</span><br><span class="line"> .<span class="title function_">then</span>(<span class="function">(<span class="params">response</span>) =></span> response.<span class="title function_">blob</span>())</span><br><span class="line"> .<span class="title function_">then</span>(<span class="function">(<span class="params">blob</span>) =></span> {</span><br><span class="line"> <span class="keyword">var</span> reader = <span class="keyword">new</span> <span class="title class_">FileReader</span>();</span><br><span class="line"> reader.<span class="property">onload</span> = <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> img.<span class="property">src</span> = <span class="variable language_">this</span>.<span class="property">result</span>;</span><br><span class="line"> <span class="title function_">resolve</span>();</span><br><span class="line"> }; <span class="comment">// <--- `this.result` contains a base64 data URI</span></span><br><span class="line"> reader.<span class="title function_">readAsDataURL</span>(blob);</span><br><span class="line"> });</span><br><span class="line"> });</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="What-we-learn"><a href="#What-we-learn" class="headerlink" title="What we learn"></a>What we learn</h1><p>During this session, we have different kinds of developers and environment so here is the conclusion as of <strong>‘Oct 2020’</strong>.</p><p>Please have in mind that Shape Detection API is still an experimental API, so the conclusion what I will write now could be not the same as what you see when you will read this post.</p><h2 id="FaceDetector"><a href="#FaceDetector" class="headerlink" title="FaceDetector"></a>FaceDetector</h2><p>Face detection works well but not everywhere. Indeed, we couldn’t make it work on those platform: </p><ul><li>Chrome Android (it’s a bug <a href="https://bugs.chromium.org/p/chromium/issues/detail?id=1107675">bc1107675</a>) When this article is written, a fix has been deployed and should be shipped with chrome 88</li><li>Chrome Linux (it has never worked <a href="https://github.com/WICG/shape-detection-api#overview">Overview api</a>)</li><li>Chrome Mac -> The landmarks aren’t well positioned (it’s a bug too <a href="https://bugs.chromium.org/p/chromium/issues/detail?id=914348">bc914348</a>)</li></ul><p>Fun fact, the face recognition is based on eyes position, it is a COVID proof API 😷😅.</p><p>If you wear glasses but big glasses, the recognition could be very lacky.</p><p><img src="/assets/2020-10-push-the-limit/gerome.jpeg" alt="Gérôme"></p><p>Indeed Gérôme as you can see wear big glasses. When he wears them, face recognition wasn’t very smooth, my Pickle Rick move with visible steps. But when he removes its glasses, the Pickle Rick follow its face.</p><p><video src="/assets/2020-10-push-the-limit/big-glasses.mp4" type="video/mp4" controls></video></p><p>The bug of Landmarks could be for the moment bypass by using <code>fast</code> mode. This bug is platform dependent. Here are the results according to the platform I could test: </p><p><img src="/assets/2020-10-push-the-limit/canary_88.0.4306.0.png" alt="Chrome Canary 88.0.4306.0 - MacOS 10.14.6"></p><p><img src="/assets/2020-10-push-the-limit/stable_86.0.4240.111.png" alt="Chrome Stable 86.0.4240.111 - MacOS 10.14.6"></p><p><img src="/assets/2020-10-push-the-limit/canary_88.0.4306.0-MacOS_10.15.7.png" alt="Chrome Canary 88.0.4306.0 - MacOS 10.15.7"></p><p><img src="/assets/2020-10-push-the-limit/stable_86.0.4240.111-fast.png" alt="Chrome Stable 86.0.4240.111 - MacOS 10.14.6 - fast mode"></p><p>You could test this at this url : <a href="https://mona-lisa.glitch.me/">https://mona-lisa.glitch.me/</a> or <a href="https://mona-lisa.glitch.me/?fast">https://mona-lisa.glitch.me/?fast</a></p><h2 id="BarCodeDetector"><a href="#BarCodeDetector" class="headerlink" title="BarCodeDetector"></a>BarCodeDetector</h2><p>The BarCodeDetector which was working previously on Android and Desktop is just working on Android for devices with the Google Play Services. It’s a limitation for the moment but we could hope that tomorrow, something based on non “native” solution will arrive: </p><blockquote><p>This API is part of the new capabilities project. Barcode detection has launched in Chrome 83 on certified devices with Google Play Services installed. Face and text detection are available behind a flag. This post will be updated as the Shape Detection API evolves</p></blockquote><p>But it works in those conditions 🤗</p><h2 id="TextDetector"><a href="#TextDetector" class="headerlink" title="TextDetector"></a>TextDetector</h2><p>This feature isn’t yet implemented in the tested devices so no-one could test it. And Google is also alerting that it’s not universally available.</p><h1 id="Credits"><a href="#Credits" class="headerlink" title="Credits"></a>Credits</h1><p>Thank you to François Beaufort who helped me debug and explore some of those APIs after the Hackathon and who reviewed this article.<br>Thank you also to Gérôme Grignon who helped me to lead this workshop.</p><script type="text/javascript" src="/assets/js_helper/jef-binomed-helper.js"></script><script type="text/javascript" src="/assets/2020-10-push-the-limit/push-the-limit.js"></script>]]></content>
<summary type="html"><p>A month ago, I organized (with a colleague of mine, <a href="https://twitter.com/geromegrignon">Gérôme Grignon</a>) a Hackathon based on </summary>
<category term="Event" scheme="http://jef.binomed.fr/categories/Event/"/>
<category term="web" scheme="http://jef.binomed.fr/tags/web/"/>
<category term="fugu" scheme="http://jef.binomed.fr/tags/fugu/"/>
</entry>
<entry>
<title>5 years of DevFest CountDown - Part 3</title>
<link href="http://jef.binomed.fr/2018/11/30/2018-11-30-5-years-of-DevFest-CountDown-part3/"/>
<id>http://jef.binomed.fr/2018/11/30/2018-11-30-5-years-of-DevFest-CountDown-part3/</id>
<published>2018-11-30T16:10:00.000Z</published>
<updated>2024-01-26T15:18:47.707Z</updated>
<content type="html"><![CDATA[<p>This article is the third of series ( <a href="https://jef.binomed.fr/2018/11/30/2018-11-30-5-years-of-DevFest-CountDown-part1">5 years of DevFest CountDown - Part 1</a> and <a href="https://jef.binomed.fr/2018/11/30/2018-11-30-5-years-of-DevFest-CountDown-part2">5 years of DevFest CountDown - Part 2</a>)</p><h1 id="2018"><a href="#2018" class="headerlink" title="2018"></a>2018</h1><div style="text-align:center; width:100%;"> <img src="/assets/2018-11-countdown/countdown2018.png" width="1000px"></div><h3 id="The-idea"><a href="#The-idea" class="headerlink" title="The idea"></a>The idea</h3><p>2018 was the year of the “space” theme. The idea was to throw planets around the sun to create a constellation of avatars! Each attendee could log through the app and use its avatar as a planet by throwing it with a very simple interface.</p><div style="text-align:center; width:100%;"> <img src="/assets/2018-11-countdown/countdown2018_player.png" width="600px"></div><p>You simply drag your finger from your avatar to the center of the screen and when you release it, your planet will be thrown to the main screen!</p><h3 id="A-New-year-a-new-challenge"><a href="#A-New-year-a-new-challenge" class="headerlink" title="A New year = a new challenge"></a>A New year = a new challenge</h3><p>As I use the countdown as a personal challenge to try lots of things, this year my biggest challenge was to learn a new framework. I wanted to learn <a href="https://vuejs.org/">Vue.js</a>. I used the package <code>@vue/cli:3.0.0</code> to serve, build my project and I used the version 2.5.x which was the latest version at the moment of this project.</p><p>Since the last 6 months, I started to be exhausted by what we can call the <em><strong>“CLI fatigue”</strong></em>. Indeed, as the frameworks evolve every month, their CLI often evolve too and when you work with several projects with different versions, having a CLI in a specific version could be a problem… I simply install the cli as a <code>devDependencies</code> and reference the CLI in the script part of my package. Here is, for example, the package.json of my project</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">{</span><br><span class="line"> <span class="string">"name"</span>: <span class="string">"countdowndevfest2018"</span>,</span><br><span class="line"> <span class="string">"version"</span>: <span class="string">"1.0.0"</span>,</span><br><span class="line"> <span class="string">"description"</span>: <span class="string">"CountDown game for DevFest Nantes 2018"</span>,</span><br><span class="line"> <span class="string">"scripts"</span>: {</span><br><span class="line"> <span class="string">"test"</span>: <span class="string">"echo \"Error: no test specified\" && exit 1"</span>,</span><br><span class="line"> <span class="string">"vue"</span>: <span class="string">"vue"</span>,</span><br><span class="line"> <span class="string">"serve"</span>: <span class="string">"vue-cli-service serve"</span>,</span><br><span class="line"> <span class="string">"clean"</span>: <span class="string">"del-cli dist/*"</span>,</span><br><span class="line"> <span class="string">"build"</span>: <span class="string">"npm run clean && vue-cli-service build && npm run cp-assets"</span>,</span><br><span class="line"> <span class="string">"lint"</span>: <span class="string">"vue-cli-service lint"</span>,</span><br><span class="line"> <span class="string">"cp-assets"</span>: <span class="string">"cpx \"public/assets/audio/*\" \"dist/assets/audio\" && cpx \"public/assets/video/*\" \"dist/assets/video/*\""</span>,</span><br><span class="line"> <span class="string">"deploy"</span>: <span class="string">"npm run build && firebase deploy --only hosting"</span>,</span><br><span class="line"> <span class="string">"deploy-ci"</span>: <span class="string">"npm run build && firebase deploy --only hosting --token \"$FIREBASE_TOKEN\" --project \"$PROJECT_NAME\""</span>,</span><br><span class="line"> <span class="string">"firebase"</span>: <span class="string">"firebase"</span></span><br><span class="line"> },</span><br><span class="line"> <span class="string">"repository"</span>: {</span><br><span class="line"> <span class="string">"type"</span>: <span class="string">"git"</span>,</span><br><span class="line"> <span class="string">"url"</span>: <span class="string">"git+https://github.com/GDG-Nantes/CountDownDevFest2018.git"</span></span><br><span class="line"> },</span><br><span class="line"> <span class="string">"keywords"</span>: [</span><br><span class="line"> <span class="string">"vuejs"</span>,</span><br><span class="line"> <span class="string">"pwa"</span>,</span><br><span class="line"> <span class="string">"game"</span></span><br><span class="line"> ],</span><br><span class="line"> <span class="string">"author"</span>: <span class="string">"jefBinomed"</span>,</span><br><span class="line"> <span class="string">"license"</span>: <span class="string">"ISC"</span>,</span><br><span class="line"> <span class="string">"bugs"</span>: {</span><br><span class="line"> <span class="string">"url"</span>: <span class="string">"https://github.com/GDG-Nantes/CountDownDevFest2018/issues"</span></span><br><span class="line"> },</span><br><span class="line"> <span class="string">"homepage"</span>: <span class="string">"https://github.com/GDG-Nantes/CountDownDevFest2018#readme"</span>,</span><br><span class="line"> <span class="string">"dependencies"</span>: {</span><br><span class="line"> <span class="string">"cpx"</span>: <span class="string">"^1.5.0"</span>,</span><br><span class="line"> <span class="string">"del-cli"</span>: <span class="string">"^1.1.0"</span>,</span><br><span class="line"> <span class="string">"firebase"</span>: <span class="string">"^5.3.0"</span>,</span><br><span class="line"> <span class="string">"firebaseui"</span>: <span class="string">"^3.4.1"</span>,</span><br><span class="line"> <span class="string">"vue"</span>: <span class="string">"^2.5.16"</span>,</span><br><span class="line"> <span class="string">"vue-router"</span>: <span class="string">"^3.0.1"</span></span><br><span class="line"> },</span><br><span class="line"> <span class="string">"devDependencies"</span>: {</span><br><span class="line"> <span class="string">"@vue/cli"</span>: <span class="string">"^3.0.0-rc.3"</span>,</span><br><span class="line"> <span class="string">"@vue/cli-plugin-babel"</span>: <span class="string">"^3.0.0-beta.15"</span>,</span><br><span class="line"> <span class="string">"@vue/cli-plugin-eslint"</span>: <span class="string">"^3.0.0-beta.15"</span>,</span><br><span class="line"> <span class="string">"@vue/cli-service"</span>: <span class="string">"^3.0.0-beta.15"</span>,</span><br><span class="line"> <span class="string">"firebase-tools"</span>: <span class="string">"^3.19.3"</span>,</span><br><span class="line"> <span class="string">"vue-template-compiler"</span>: <span class="string">"^2.5.16"</span></span><br><span class="line"> },</span><br><span class="line"> <span class="string">"eslintConfig"</span>: {</span><br><span class="line"> <span class="string">"root"</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="string">"env"</span>: {</span><br><span class="line"> <span class="string">"node"</span>: <span class="literal">true</span></span><br><span class="line"> },</span><br><span class="line"> <span class="string">"extends"</span>: [</span><br><span class="line"> <span class="string">"plugin:vue/essential"</span>,</span><br><span class="line"> <span class="string">"eslint:recommended"</span></span><br><span class="line"> ],</span><br><span class="line"> <span class="string">"rules"</span>: {},</span><br><span class="line"> <span class="string">"parserOptions"</span>: {</span><br><span class="line"> <span class="string">"parser"</span>: <span class="string">"babel-eslint"</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> <span class="string">"postcss"</span>: {</span><br><span class="line"> <span class="string">"plugins"</span>: {</span><br><span class="line"> <span class="string">"autoprefixer"</span>: {}</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> <span class="string">"browserslist"</span>: [</span><br><span class="line"> <span class="string">"> 1%"</span>,</span><br><span class="line"> <span class="string">"last 2 versions"</span>,</span><br><span class="line"> <span class="string">"not ie <= 8"</span></span><br><span class="line"> ]</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>As you can see, I referenced the cli I needed and I was sure that the version of vue/cli wouldn’t interfere with any other of my cli.</p><h3 id="The-new-Architecture"><a href="#The-new-Architecture" class="headerlink" title="The new Architecture"></a>The new Architecture</h3><div style="text-align:center; width:100%;"> <img src="/assets/2018-11-countdown/Galaxy_Archi.png" width="800px"></div><p>As you can see, I removed lots of elements to focus on my code. And I wanted to go back to something compatible with the <a href="https://en.wikipedia.org/wiki/KISS_principle">KISS principle</a></p><p>As firebase evolves each year, in 2016, when I started to use it, Firestore wasn’t available and we couldn’t listen to change on the tree. Back in 2016, I decided to use the realtime database. But in 2018, firestore offered me all I need :</p><ul><li>A database with a higher quota for the storage: 1 GB</li><li>A number of simultaneous connection very high 1,000,000</li><li>The possibility to be notified when a change is done to the tree (even if in realtime, we could do that)</li></ul><p>It was more than I really needed, so I used it in replacement of realtime database.</p><p>One of the pain point I had to face this year was to be attentive to the performances!! Indeed, I wanted to show a high number of planets on screen. Every planet is following an ellipse and I have to calculate for each planet if it enter in collision with another planet. All those calculations could cost times so I decided to use a Web Worker to do all the calculations and to notify the app with a new model as soon as the calculations were done.</p><div style="text-align:center; width:100%;"> <img src="/assets/2018-11-countdown/Galaxy_Validation.png" width="800px"></div><p>To summarize:</p><ol><li>A player launchs a planet</li><li>The planet is added/updated in the firestore tree</li><li>The Countdown screen is notified and asks to the webworker to add the new planet</li></ol><p>In parallel</p><ol><li>The webworker calculates the position of the planets, the collisions, updates the model</li><li>The Countdown screen receives the data.</li><li>When the <code>requestAnimationFrame</code>is called, the Countdown screen reads the current model and displays it</li></ol><p>All the animations, stars, shine effect, are just maths and effects with the Canvas, I won’t explain how I do this. If you are interested in that, check the source code (end of this section).</p><h3 id="Data-Structure-and-security"><a href="#Data-Structure-and-security" class="headerlink" title="Data Structure and security"></a>Data Structure and security</h3><p>To secure my paths and data, I used firebase authentication and path configuration in Vue:</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">secureRoute</span> = (<span class="params">to, <span class="keyword">from</span>, next</span>) => {</span><br><span class="line"> <span class="keyword">const</span> currentRoute = to.<span class="property">path</span>;</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">from</span>.<span class="property">path</span> === <span class="string">'/wait'</span>) {</span><br><span class="line"> <span class="title function_">next</span>();</span><br><span class="line"> }<span class="keyword">else</span> {</span><br><span class="line"> <span class="title function_">next</span>(<span class="string">'/wait'</span>);</span><br><span class="line"> firebase.<span class="title function_">auth</span>().<span class="title function_">onAuthStateChanged</span>(<span class="function">(<span class="params">user</span>) =></span> {</span><br><span class="line"> <span class="keyword">if</span>(user) {</span><br><span class="line"> <span class="title function_">next</span>(currentRoute);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="title function_">next</span>(<span class="string">'/auth'</span>);</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="keyword">const</span> routes = [</span><br><span class="line"> {</span><br><span class="line"> <span class="comment">// The main page for users</span></span><br><span class="line"> <span class="attr">path</span>: <span class="string">'/'</span>,</span><br><span class="line"> <span class="attr">component</span>: <span class="title class_">Game</span>,</span><br><span class="line"> <span class="attr">beforeEnter</span>: secureRoute</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="comment">// The page to display when the countdown is over</span></span><br><span class="line"> <span class="attr">path</span>: <span class="string">'/final'</span>,</span><br><span class="line"> <span class="attr">component</span>: <span class="title class_">Final</span></span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="comment">// The page to show when the user is waiting for his connection</span></span><br><span class="line"> <span class="attr">path</span>: <span class="string">'/wait'</span>,</span><br><span class="line"> <span class="attr">component</span>: <span class="title class_">Wait</span></span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="comment">// The page of authentication</span></span><br><span class="line"> <span class="attr">path</span>: <span class="string">'/auth'</span>,</span><br><span class="line"> <span class="attr">component</span>: <span class="title class_">Auth</span></span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="comment">// The main screen with the countdown</span></span><br><span class="line"> <span class="attr">path</span>: <span class="string">'/countdown'</span>,</span><br><span class="line"> <span class="attr">component</span>: <span class="title class_">Countdown</span>,</span><br><span class="line"> <span class="attr">beforeEnter</span>: secureRoute</span><br><span class="line"> }</span><br><span class="line">]</span><br></pre></td></tr></table></figure><p>The idea was to check for specific route (<code>/countdown</code>, <code>/game</code>) if the user is authenticated. If not, I redirect the user to the authentication route. The period while the application is waiting for checking if the user is authenticated, I redirect him/her/them to a waiting screen. I’m not sure if it’s the best practice or not but it works pretty well 😇.</p><p>The <code>/countdown</code> route should be shown only to “Admins” so I secured this page with this redirection</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// Mount method of my CountDown component</span></span><br><span class="line"><span class="title function_">mounted</span>(<span class="params"></span>) {</span><br><span class="line"> firestore.<span class="title function_">collection</span>(<span class="string">"admins"</span>).<span class="title function_">get</span>(<span class="string">'adminList'</span>)</span><br><span class="line"> .<span class="title function_">then</span>(<span class="function">()=></span>{</span><br><span class="line"> <span class="comment">// console.debug('Admin Loggued :)');</span></span><br><span class="line"> })</span><br><span class="line"> .<span class="title function_">catch</span>(<span class="function">(<span class="params">error</span>) =></span>{</span><br><span class="line"> <span class="comment">// eslint-disable-next-line no-console</span></span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">error</span>(error);</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">$router</span>.<span class="title function_">push</span>(<span class="string">'/'</span>)</span><br><span class="line"> });</span><br><span class="line">},</span><br></pre></td></tr></table></figure><p>Indeed, I can consider that an error here is thrown when the current user has not the permission to see the collection “admins”. This leads me to the protection of the data. Here is the structure of my data</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// Collection 'admins'</span></span><br><span class="line">{</span><br><span class="line"> <span class="attr">adminList</span>: {</span><br><span class="line"> admin.<span class="property">email</span><span class="number">.1</span>@email.<span class="property">com</span>: <span class="literal">true</span>,</span><br><span class="line"> admin.<span class="property">email</span><span class="number">.2</span>@email.<span class="property">com</span>: <span class="literal">true</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// Collection 'planets'</span></span><br><span class="line">{</span><br><span class="line"> <span class="attr">UID_OfUserLoggued1</span>: {</span><br><span class="line"> <span class="attr">angle</span>: <span class="number">0</span>,</span><br><span class="line"> <span class="attr">collision</span>: <span class="literal">false</span>,</span><br><span class="line"> ...</span><br><span class="line"> },</span><br><span class="line"> <span class="attr">UID_OfUserLoggued2</span>: {</span><br><span class="line"> <span class="attr">angle</span>: <span class="number">0</span>,</span><br><span class="line"> <span class="attr">collision</span>: <span class="literal">false</span>,</span><br><span class="line"> ...</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>To secure those data, I used these firebase rules:</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">service cloud.<span class="property">firestore</span> {</span><br><span class="line"> match /databases/{database}/documents {</span><br><span class="line"> <span class="comment">// Generic method that checks if the email of the currently authenticated user is contained in the admin collection</span></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">isAdmin</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> request.<span class="property">auth</span> != <span class="literal">null</span></span><br><span class="line"> && <span class="title function_">get</span>(<span class="regexp">/databases/</span>$(database)/documents/admins/adminList).<span class="property">data</span>[request.<span class="property">auth</span>.<span class="property">token</span>.<span class="property">email</span>] == <span class="literal">true</span></span><br><span class="line"> && request.<span class="property">auth</span>.<span class="property">token</span>.<span class="property">email_verified</span> == <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// The admin collection is in read only for the admins</span></span><br><span class="line"> match /admins/{<span class="variable language_">document</span>=**} {</span><br><span class="line"> allow <span class="attr">read</span>: <span class="keyword">if</span> <span class="title function_">isAdmin</span>();</span><br><span class="line"> allow write, <span class="keyword">delete</span>, <span class="attr">update</span>: <span class="keyword">if</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// To update a planet, you have to be the user that create it or to be an admin. Everyone logged can read the data of a planet (because there is nothing critical in it)</span></span><br><span class="line"> match /planets/{planetId} {</span><br><span class="line"> allow update, <span class="attr">delete</span>: <span class="keyword">if</span> request.<span class="property">auth</span>.<span class="property">uid</span> == planetId</span><br><span class="line"> || <span class="title function_">isAdmin</span>();</span><br><span class="line"> allow read, <span class="attr">create</span>: <span class="keyword">if</span> request.<span class="property">auth</span>.<span class="property">uid</span> != <span class="literal">null</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>With these few lines, I secured my application and my data 💪.</p><h3 id="Few-enhancements"><a href="#Few-enhancements" class="headerlink" title="Few enhancements"></a>Few enhancements</h3><p>Although every year I reuse the codebase for the timer, the audio player, … This year I wanted to fix and enhance a little bit the class. I focused my enhancement to the Audio Player and the timer.</p><h4 id="The-Timer"><a href="#The-Timer" class="headerlink" title="The Timer"></a>The Timer</h4><p>Before this year, I never had to create a class for it, this has to be fixed.</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="meta">'use strict'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">class</span> <span class="title class_">Timer</span> {</span><br><span class="line"> <span class="title function_">constructor</span>(<span class="params">callback</span>){</span><br><span class="line"> <span class="comment">// Target Time : '2018-10-18T09:00:00'</span></span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">targetDate</span> = <span class="keyword">new</span> <span class="title class_">Date</span>(<span class="title class_">Date</span>.<span class="title function_">now</span>() + <span class="number">30</span> * <span class="number">1000</span> + <span class="number">120</span> * <span class="number">1000</span>);</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">callback</span> = callback;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="title function_">checkTime</span>();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"><span class="title function_">checkTime</span>(<span class="params"></span>) {</span><br><span class="line"><span class="keyword">const</span> now = <span class="title class_">Date</span>.<span class="title function_">now</span>();</span><br><span class="line"><span class="keyword">if</span> (now > <span class="variable language_">this</span>.<span class="property">targetDate</span>.<span class="title function_">getTime</span>()) {</span><br><span class="line"><span class="variable language_">this</span>.<span class="title function_">callback</span>({</span><br><span class="line"><span class="attr">type</span>: <span class="string">'endCountDown'</span>,</span><br><span class="line"><span class="attr">value</span>: <span class="literal">true</span>,</span><br><span class="line">});</span><br><span class="line"><span class="keyword">return</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> diff = <span class="variable language_">this</span>.<span class="property">targetDate</span>.<span class="title function_">getTime</span>() - now;</span><br><span class="line"><span class="keyword">const</span> minutes = <span class="keyword">new</span> <span class="title class_">Intl</span>.<span class="title class_">NumberFormat</span>(<span class="string">'fr'</span>, {</span><br><span class="line"><span class="attr">minimumIntegerDigits</span>: <span class="number">2</span>,</span><br><span class="line"><span class="attr">useGrouping</span>: <span class="literal">false</span>,</span><br><span class="line">}).<span class="title function_">format</span>(<span class="title class_">Math</span>.<span class="title function_">floor</span>(diff / (<span class="number">60</span> * <span class="number">1000</span>)));</span><br><span class="line"><span class="keyword">const</span> seconds = <span class="keyword">new</span> <span class="title class_">Intl</span>.<span class="title class_">NumberFormat</span>(<span class="string">'fr'</span>, {</span><br><span class="line"><span class="attr">minimumIntegerDigits</span>: <span class="number">2</span>,</span><br><span class="line"><span class="attr">useGrouping</span>: <span class="literal">false</span>,</span><br><span class="line">}).<span class="title function_">format</span>(<span class="title class_">Math</span>.<span class="title function_">floor</span>((diff % (<span class="number">60</span> * <span class="number">1000</span>)) / <span class="number">1000</span>));</span><br><span class="line"><span class="keyword">const</span> lastMinute = diff < <span class="number">60</span> * <span class="number">1000</span>;</span><br><span class="line"><span class="variable language_">this</span>.<span class="title function_">callback</span>({</span><br><span class="line"><span class="attr">type</span>: <span class="string">'time'</span>,</span><br><span class="line"><span class="attr">value</span>: {</span><br><span class="line">minutes,</span><br><span class="line">seconds,</span><br><span class="line">lastMinute,</span><br><span class="line">diff,</span><br><span class="line">},</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="variable language_">window</span>.<span class="title function_">requestAnimationFrame</span>(<span class="variable language_">this</span>.<span class="property">checkTime</span>.<span class="title function_">bind</span>(<span class="variable language_">this</span>));</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>This timer is updated very often and gives the delta to the main screen.</p><h4 id="The-Audio-player"><a href="#The-Audio-player" class="headerlink" title="The Audio player"></a>The Audio player</h4><p>One of our problem each year is the timing. We play music and we want a specific music to be played at the end. Let me explain it more easily. If we start the countdown 45min before the beginning of the Keynote and we want the last song to be played to be, for example, ACDC - Thunderstock and we want the countdown to show <strong>00:00</strong> when Thunderstock is finishing… It was not very easy because we have to calculate the right time to start our playlist, be sure to not stop it, …</p><p>So I got an idea. What if I can specify what is the last song, its duration and what if the countdown automatically switched to this song when it’s the right moment? That’s why I did an evolution in my AudioPlayer class</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="meta">'use strict'</span></span><br><span class="line"><span class="keyword">import</span> {</span><br><span class="line"> <span class="variable constant_">PLAYLIST</span>,</span><br><span class="line"> <span class="variable constant_">LASTS_SONGS_PLAYLIST</span></span><br><span class="line">} <span class="keyword">from</span> <span class="string">'./playlist.js'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Class for playing music</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * We create an invisible audio element and we play music on it</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">AudioPlayer</span> {</span><br><span class="line"> <span class="title function_">constructor</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">indexPlayList</span> = <span class="number">0</span>;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">currentIndex</span> = <span class="number">0</span>;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">audioElt</span> = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">'audio'</span>);</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">audioElt</span>.<span class="property">style</span>.<span class="property">display</span> = <span class="string">'none'</span>;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">currentPlaylist</span> = <span class="variable constant_">PLAYLIST</span>;</span><br><span class="line"> <span class="variable language_">document</span>.<span class="property">body</span>.<span class="title function_">appendChild</span>(<span class="variable language_">this</span>.<span class="property">audioElt</span>);</span><br><span class="line"> <span class="variable language_">window</span>.<span class="title function_">addEventListener</span>(<span class="string">'beforeunload'</span>, <span class="variable language_">this</span>.<span class="property">_unload</span>.<span class="title function_">bind</span>(<span class="variable language_">this</span>));</span><br><span class="line"> <span class="variable language_">this</span>.<span class="title function_">_startPlayer</span>();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="title function_">_startPlayer</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="variable language_">localStorage</span>[<span class="string">'devfestCountdown-LastSong'</span>]) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">indexPlayList</span> = +<span class="variable language_">localStorage</span>[<span class="string">'devfestCountdown-LastSong'</span>];</span><br><span class="line"> <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">indexPlayList</span> >= <span class="variable language_">this</span>.<span class="property">currentPlaylist</span>.<span class="property">length</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="title function_">_nextSong</span>();</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="title function_">_playSound</span>(<span class="string">`./assets/audio/<span class="subst">${<span class="variable language_">this</span>.currentPlaylist[<span class="variable language_">this</span>.indexPlayList]}</span>`</span>);</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">audioElt</span>.<span class="property">currentTime</span> = +<span class="variable language_">localStorage</span>[<span class="string">'devfestCountdown-currentTime'</span>];</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">indexPlayList</span> = -<span class="number">1</span>;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="title function_">_nextSong</span>();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="title function_">_unload</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">localStorage</span>[<span class="string">'devfestCountdown-LastSong'</span>] = <span class="string">`<span class="subst">${<span class="variable language_">this</span>.currentIndex}</span>`</span>;</span><br><span class="line"> <span class="variable language_">localStorage</span>[<span class="string">'devfestCountdown-currentTime'</span>] = <span class="string">`<span class="subst">${<span class="variable language_">this</span>.audioElt.currentTime}</span>`</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Play a song according to the url of song</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="title function_">_playSound</span>(<span class="params">url</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">audioElt</span>.<span class="title function_">pause</span>();</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">audioElt</span>.<span class="property">src</span> = url;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">audioElt</span>.<span class="title function_">play</span>();</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">audioElt</span>.<span class="property">onended</span> = <span class="variable language_">this</span>.<span class="property">_nextSong</span>.<span class="title function_">bind</span>(<span class="variable language_">this</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Skip to the next song</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="title function_">_nextSong</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">currentIndex</span> = <span class="title class_">Math</span>.<span class="title function_">max</span>(<span class="variable language_">this</span>.<span class="property">indexPlayList</span>, <span class="number">0</span>);</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">indexPlayList</span> = (<span class="variable language_">this</span>.<span class="property">indexPlayList</span> + <span class="number">1</span>) % <span class="variable language_">this</span>.<span class="property">currentPlaylist</span>.<span class="property">length</span>;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="title function_">_playSound</span>(<span class="string">`./assets/audio/<span class="subst">${<span class="variable language_">this</span>.currentPlaylist[<span class="variable language_">this</span>.indexPlayList]}</span>`</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (err) {</span><br><span class="line"> <span class="comment">// eslint-disable-next-line no-console</span></span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">error</span>(err);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Update the sound volume of audio element</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="title function_">manageSoundVolume</span>(<span class="params">delta</span>) {</span><br><span class="line"> <span class="keyword">if</span> (delta < <span class="number">10</span> * <span class="number">1000</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">audioElt</span>.<span class="property">volume</span> = <span class="title class_">Math</span>.<span class="title function_">min</span>(<span class="title class_">Math</span>.<span class="title function_">max</span>(<span class="number">0</span>, delta / (<span class="number">10</span> * <span class="number">1000</span>)), <span class="number">0.7</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="title function_">manageVolumeFromPercent</span>(<span class="params">percent</span>) {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (percent > <span class="number">0</span>){</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">audioElt</span>.<span class="property">volume</span> = <span class="title class_">Math</span>.<span class="title function_">min</span>(percent, <span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="title function_">switchToLastsSongPlaylist</span>(<span class="params"></span>){</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">audioElt</span>.<span class="property">volume</span> = <span class="number">1</span>;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">indexPlayList</span> = <span class="number">0</span>;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">currentPlaylist</span> = <span class="variable constant_">LASTS_SONGS_PLAYLIST</span>;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="title function_">_nextSong</span>();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>I had some controls to deal with the sound volume and to switch to the playlist of last songs. The code that execute those controls is in a separate class because it’s not the rule of the AudioPlayer to know when to change! The code that dealt with the timing was in my CountDown component:</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> timeBeforeLastSongs = <span class="number">60</span> * <span class="number">1000</span>; <span class="comment">// 1 Minute</span></span><br><span class="line"><span class="keyword">const</span> dropTimeForLastSong = <span class="number">5</span> * <span class="number">1000</span>; <span class="comment">// 5 sec</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> {</span><br><span class="line"><span class="attr">name</span>: <span class="string">'countdown'</span>,</span><br><span class="line"><span class="attr">components</span>: { <span class="title class_">Galaxy</span>, <span class="title class_">ScoreList</span>, <span class="title class_">Timer</span> },</span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line"><span class="attr">methods</span>: {</span><br><span class="line"><span class="comment">/// ...</span></span><br><span class="line"><span class="title function_">timeUpdate</span>(<span class="params">event</span>) {</span><br><span class="line"><span class="comment">// If we're in the last song delay, we first drop the sound of current sound before</span></span><br><span class="line"><span class="keyword">if</span> (</span><br><span class="line">event.<span class="property">diff</span> < timeBeforeLastSongs &&</span><br><span class="line">event.<span class="property">diff</span> > timeBeforeLastSongs - dropTimeForLastSong</span><br><span class="line">) {</span><br><span class="line"> <span class="comment">// We simulate a kind of fader to switch to last song</span></span><br><span class="line"><span class="keyword">const</span> adjustDiff =</span><br><span class="line">event.<span class="property">diff</span> - (timeBeforeLastSongs - dropTimeForLastSong);</span><br><span class="line"><span class="variable language_">this</span>.<span class="property">audioPlayer</span>.<span class="title function_">manageVolumeFromPercent</span>(</span><br><span class="line">adjustDiff / dropTimeForLastSong,</span><br><span class="line">);</span><br><span class="line">} <span class="keyword">else</span> <span class="keyword">if</span> (event.<span class="property">diff</span> < timeBeforeLastSongs && !<span class="variable language_">this</span>.<span class="property">switchToLastsSongs</span>) {</span><br><span class="line"> <span class="comment">// When it's time, we switch to last song</span></span><br><span class="line"><span class="variable language_">this</span>.<span class="property">audioPlayer</span>.<span class="title function_">switchToLastsSongPlaylist</span>();</span><br><span class="line"><span class="variable language_">this</span>.<span class="property">switchToLastsSongs</span> = <span class="literal">true</span>;</span><br><span class="line">} <span class="keyword">else</span> <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">audioPlayer</span>) {</span><br><span class="line"> <span class="comment">// We wait for the last 10 seconds to drop down the volume</span></span><br><span class="line"><span class="variable language_">this</span>.<span class="property">audioPlayer</span>.<span class="title function_">manageSoundVolume</span>(event.<span class="property">diff</span>);</span><br><span class="line">}</span><br><span class="line">},</span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line">},</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line">};</span><br></pre></td></tr></table></figure><h3 id="My-Conclusion"><a href="#My-Conclusion" class="headerlink" title="My Conclusion"></a>My Conclusion</h3><p>My first challenge was to try Vue.js and my conclusion is VueJS is good framework / library to prototype application but not the best solution when you have an application with a high frame rate like 30fps. The main problem comes from the fact that my data were refreshed very often… More often than the inner mechanism of rendering of Vue. So Vue destroys and recreates too many times the HTML Nodes.</p><p>For example, to be performant, I had to change this code:</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"> <<span class="title class_">Score</span></span><br><span class="line"> v-<span class="keyword">for</span>=<span class="string">"planet in planets.slice(0,10)"</span></span><br><span class="line"> :key=<span class="string">"planet.id"</span></span><br><span class="line"> v-<span class="attr">bind</span>:planet=<span class="string">"planet"</span></span><br><span class="line">></<span class="title class_">Score</span>></span><br></pre></td></tr></table></figure><p>To this:</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><<span class="title class_">Score</span></span><br><span class="line"> v-<span class="keyword">if</span>=<span class="string">"planets.length > 0"</span></span><br><span class="line"> v-<span class="attr">bind</span>:planet=<span class="string">"planets[0]"</span></span><br><span class="line">></<span class="title class_">Score</span>></span><br><span class="line"><span class="language-xml"><span class="tag"><<span class="name">Score</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">v-if</span>=<span class="string">"planets.length > 1"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">v-bind:planet</span>=<span class="string">"planets[1]"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">></span><span class="tag"></<span class="name">Score</span>></span></span></span><br><span class="line"><span class="language-xml"><span class="tag"><<span class="name">Score</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">v-if</span>=<span class="string">"planets.length > 2"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">v-bind:planet</span>=<span class="string">"planets[2]"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">></span><span class="tag"></<span class="name">Score</span>></span></span></span><br><span class="line"><span class="language-xml"><span class="tag"><<span class="name">Score</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">v-if</span>=<span class="string">"planets.length > 3"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">v-bind:planet</span>=<span class="string">"planets[3]"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">></span><span class="tag"></<span class="name">Score</span>></span></span></span><br><span class="line"><span class="language-xml"><span class="tag"><<span class="name">Score</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">v-if</span>=<span class="string">"planets.length > 4"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">v-bind:planet</span>=<span class="string">"planets[4]"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">></span><span class="tag"></<span class="name">Score</span>></span></span></span><br></pre></td></tr></table></figure><p>And I also had to give data to a children component through a method exposed in the child component instead of using the properties. This problem comes to serialization/deserialization of the object and causes a re-rendering of the child component where my basic rendering was made by the canvas.</p><p>For the next year, I think that I will continue to work with vanillaJS because the use case of the countdown is each time to match with the mindset of a framework. I’m not saying that Vue is a bad framework, but I really think that it wasn’t the best choice for my project.</p><h3 id="Code-Demo"><a href="#Code-Demo" class="headerlink" title="Code & Demo"></a>Code & Demo</h3><p>You can find the code here: <a href="https://github.com/GDG-Nantes/CountDownDevFest2018">CountDown DevFest 2018</a>.</p><p>If you want to see it in action, have a look here <a href="http://gdg-nantes.github.io/CountDownDevFest2018/">CountDown 2018</a></p><h1 id="2019"><a href="#2019" class="headerlink" title="2019?"></a>2019?</h1><p>I don’t know yet what I will write for 2019 but it will be maybe a new game based on vanilla. Stay tuned 🤘</p><p>PS : A huge thanks to <a href="https://twitter.com/elainedbatista">Elaine Dias Batista</a> for having taking time to read and correct this article.</p><!-- Imports to use for interactivité --><script type="text/javascript" src="/assets/js_helper/jef-binomed-helper.js"></script><script type="module" src="/assets/2018-11-countdown/countdown.js"></script>]]></content>
<summary type="html"><p>This article is the third of series ( <a href="https://jef.binomed.fr/2018/11/30/2018-11-30-5-years-of-DevFest-CountDown-part1">5 years o</summary>
<category term="Conference" scheme="http://jef.binomed.fr/categories/Conference/"/>
<category term="DevFest" scheme="http://jef.binomed.fr/tags/DevFest/"/>
<category term="Canvas" scheme="http://jef.binomed.fr/tags/Canvas/"/>
</entry>
<entry>
<title>5 years of DevFest CountDown - Part 2</title>
<link href="http://jef.binomed.fr/2018/11/30/2018-11-30-5-years-of-DevFest-CountDown-part2/"/>
<id>http://jef.binomed.fr/2018/11/30/2018-11-30-5-years-of-DevFest-CountDown-part2/</id>
<published>2018-11-30T16:05:00.000Z</published>
<updated>2024-01-26T15:18:47.706Z</updated>
<content type="html"><![CDATA[<p>This article is the second of series ( <a href="https://jef.binomed.fr/2018/11/30/2018-11-30-5-years-of-DevFest-CountDown-part1">5 years of DevFest CountDown - Part 1</a>)</p><h1 id="2016"><a href="#2016" class="headerlink" title="2016"></a>2016</h1><h3 id="The-year-of-fail-😅"><a href="#The-year-of-fail-😅" class="headerlink" title="The year of fail 😅"></a>The year of fail 😅</h3><div style="text-align:center; width:100%;"> <img src="/assets/2018-11-countdown/gdg_logo_legonnary.png" width="400px"></div><p>This year had the theme of ‘Lego’ so I had in mind an interactive game where people draw some “pixel art” with Lego bricks and submit them for the main screen. It was a good idea but my code this year wasn’t robust enough and the real-time database contained some corrupted data and everything crashed 30min before the official launch!</p><p>We had to hide the game and we just displayed the basic countdown with music. If you are interested in the architecture, I wrote a series of article about it: <a href="https://jef.binomed.fr/2016/12/23/2016-12-23-legonnary">Legonnary</a> ⚠️ those articles are in French 🇫🇷 for the moment. I will try to translate them when I have time.</p><p>Just notice that it was a PWA application that uses Firebase (real-time database, hosting, auth).</p><div style="text-align:center; width:100%;"> <img src="/assets/2018-11-countdown/Legonnary_Archi.png" width="800px"></div><p>I created 4 web pages:</p><ul><li>The game that the user used</li><li>The interface that the moderator used</li><li>The interface that corresponds to the main screen with the countdown</li><li>The interface that shows a summary of all the generated drawings</li></ul><h3 id="The-year-of-ES6"><a href="#The-year-of-ES6" class="headerlink" title="The year of ES6"></a>The year of ES6</h3><p>As ES6 was starting to be everywhere, I started to rethink a little bit my code to make it more reusable.</p><p>I created a class for the audio player :</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="meta">'use strict'</span></span><br><span class="line"><span class="keyword">import</span> {</span><br><span class="line"> <span class="variable constant_">PLAYLIST</span></span><br><span class="line">} <span class="keyword">from</span> <span class="string">'./playlist.js'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Class for playing music</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * We create an invisible audio element and we play music on it</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">AudioPlayer</span> {</span><br><span class="line"> <span class="title function_">constructor</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">indexPlayList</span> = <span class="number">0</span>;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">currentIndex</span> = <span class="number">0</span>;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">audioElt</span> = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">'audio'</span>);</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">audioElt</span>.<span class="property">style</span>.<span class="property">display</span> = <span class="string">'none'</span>;</span><br><span class="line"> <span class="variable language_">document</span>.<span class="property">body</span>.<span class="title function_">appendChild</span>(<span class="variable language_">this</span>.<span class="property">audioElt</span>);</span><br><span class="line"> <span class="variable language_">window</span>.<span class="title function_">addEventListener</span>(<span class="string">'beforeunload'</span>, <span class="variable language_">this</span>.<span class="property">_unload</span>.<span class="title function_">bind</span>(<span class="variable language_">this</span>));</span><br><span class="line"> <span class="variable language_">this</span>.<span class="title function_">_startPlayer</span>();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="title function_">_startPlayer</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="variable language_">localStorage</span>[<span class="string">'devfestCountdown-LastSong'</span>]) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">indexPlayList</span> = +<span class="variable language_">localStorage</span>[<span class="string">'devfestCountdown-LastSong'</span>];</span><br><span class="line"> <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">indexPlayList</span> >= <span class="variable constant_">PLAYLIST</span>.<span class="property">length</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="title function_">_nextSong</span>();</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="title function_">_playSound</span>(<span class="string">`./assets/audio/<span class="subst">${PLAYLIST[<span class="variable language_">this</span>.indexPlayList]}</span>`</span>);</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">audioElt</span>.<span class="property">currentTime</span> = +<span class="variable language_">localStorage</span>[<span class="string">'devfestCountdown-currentTime'</span>];</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="title function_">_nextSong</span>();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="title function_">_unload</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">localStorage</span>[<span class="string">'devfestCountdown-LastSong'</span>] = <span class="string">`<span class="subst">${<span class="variable language_">this</span>.currentIndex}</span>`</span>;</span><br><span class="line"> <span class="variable language_">localStorage</span>[<span class="string">'devfestCountdown-currentTime'</span>] = <span class="string">`<span class="subst">${<span class="variable language_">this</span>.audioElt.currentTime}</span>`</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Play a song according to the url of song</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="title function_">_playSound</span>(<span class="params">url</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">audioElt</span>.<span class="title function_">pause</span>();</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">audioElt</span>.<span class="property">src</span> = url;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">audioElt</span>.<span class="title function_">play</span>();</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">audioElt</span>.<span class="property">onended</span> = <span class="variable language_">this</span>.<span class="property">_nextSong</span>.<span class="title function_">bind</span>(<span class="variable language_">this</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Skip to the next song</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="title function_">_nextSong</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">currentIndex</span> = <span class="variable language_">this</span>.<span class="property">indexPlayList</span>;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="title function_">_playSound</span>(<span class="string">`./assets/audio/<span class="subst">${PLAYLIST[<span class="variable language_">this</span>.indexPlayList]}</span>`</span>);</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">indexPlayList</span> = (<span class="variable language_">this</span>.<span class="property">indexPlayList</span> + <span class="number">1</span>) % <span class="variable constant_">PLAYLIST</span>.<span class="property">length</span>;</span><br><span class="line"> } <span class="keyword">catch</span> (err) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">error</span>(err);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Update the sound volume of audio element</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="title function_">manageSoundVolume</span>(<span class="params">delta</span>) {</span><br><span class="line"> <span class="keyword">if</span> (delta < <span class="number">10</span> * <span class="number">1000</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">audioElt</span>.<span class="property">volume</span> = <span class="title class_">Math</span>.<span class="title function_">min</span>(<span class="title class_">Math</span>.<span class="title function_">max</span>(<span class="number">0</span>, delta / (<span class="number">10</span> * <span class="number">1000</span>)), <span class="number">0.5</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>And the same thing for the video player:</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="meta">'use strict'</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Class for playing video</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">VideoPlayer</span> {</span><br><span class="line"> <span class="title function_">constructor</span>(<span class="params">parentElt, callBackEnd</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">videoElt</span> = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">'video'</span>);</span><br><span class="line"> parentElt.<span class="title function_">appendChild</span>(<span class="variable language_">this</span>.<span class="property">videoElt</span>);</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">videoName</span> = <span class="string">'MotionDevfest2017_HQ.mp4'</span>;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">callBackEnd</span> = callBackEnd;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Play the video</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="title function_">playVideo</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">videoElt</span>.<span class="title function_">pause</span>();</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">videoElt</span>.<span class="property">src</span> = <span class="string">`./assets/video/<span class="subst">${<span class="variable language_">this</span>.videoName}</span>`</span>;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">videoElt</span>.<span class="title function_">play</span>();</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">videoElt</span>.<span class="property">onended</span> = <span class="variable language_">this</span>.<span class="property">callBackEnd</span>.<span class="title function_">bind</span>(<span class="variable language_">this</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="Code"><a href="#Code" class="headerlink" title="Code"></a>Code</h3><p>You can find the code here: <a href="https://github.com/GDG-Nantes/CountDownDevFest2016">CountDown DevFest 2016</a>.</p><h1 id="2017"><a href="#2017" class="headerlink" title="2017"></a>2017</h1><div style="text-align:center; width:100%;"> <img src="/assets/2018-11-countdown/DevFestDraw_screen.png" width="800px"></div><h3 id="The-revenge"><a href="#The-revenge" class="headerlink" title="The revenge!!"></a>The revenge!!</h3><p>As in 2016, I created a whole application with moderation, a display on the screen… I didn’t want to put everything I did into the trash. So I reused the code, did some evolutions and wanted to complexify the project. Indeed, I introduced some machine learning in order to try to recognize what the attendees drew. Here is the workflow of the application</p><div style="text-align:center; width:100%;"> <img src="/assets/2018-11-countdown/DevFestDraw_Validation.png" width="800px"></div><ol><li>A user submits a drawing</li><li>The drawing is uploaded to firebase, caught by a cloud function</li><li>The cloud function asks a machine learning model to analyze the image and try to classify it.</li><li>The moderator sees the drawing to validate (I wanted to be sure that the content will not break the code of conduct 😅)</li><li>The drawing is moved somewhere in the tree of real-time database</li><li>All valid drawing are shown on the main screen with their classifications.</li></ol><h3 id="Recap-of-architecture"><a href="#Recap-of-architecture" class="headerlink" title="Recap of architecture"></a>Recap of architecture</h3><div style="text-align:center; width:100%;"> <img src="/assets/2018-11-countdown/DevFestDraw_Archi.png" width="800px"></div><p>I tried to use the power of serverless as much as I could. So what I add this year was the additionnal services:</p><ul><li>A cloud function to listen to new drawing in the firebase tree based on Firebase Admin SDK</li><li>A Machine learning model utilized to recognize the drawing based on Cloud ML</li><li>Google Cloud Storage to save the drawings</li></ul><h3 id="Machine-learning-addon"><a href="#Machine-learning-addon" class="headerlink" title="Machine learning addon"></a>Machine learning addon</h3><p>The biggest addition of 2017 was the machine learning detection. So to use it, I created a cloud function that used the Firebase Admin SDK to listen to new additions of drawings :</p><ol><li>The user creates a drawing.</li><li>He submits it and the application uploads the drawing to cloud storage</li><li>The cloud function starts</li><li>The drawing is compressed and converted to a greyscale image of 28x28 pixels (it’s because the model was trained to recognize a grayscale image of this size and to be as efficient as we can)</li><li>The model is interrogated</li><li>The drawing is classified</li><li>The cloud function updates the firebase model in order to change the state of the drawing to continue the workflow.</li></ol><p>As I’m not a specialist on Machine Learning, I Asked some help from a Googler from New York <a href="https://twitter.com/YufengG">Yufeng Guo</a>. He did all the stuff of training the model. He trained a machine learning model based on ‘black and white’ image of 28x28 pixels. I had to do a conversion between what I received and what I sent to the ML engine. The image:</p><div style="text-align:center; width:100%;"> <img src="/assets/2018-11-countdown/DevFestDraw_before.png" ></div><p>Will be sent to the engine like this:</p><div style="text-align:center; width:100%;"> <img src="/assets/2018-11-countdown/DevFestDraw_after.png" ></div><p>As you can imagine there will be a lot of misunderstanding of the model, It was the game 😃</p><p>Here is the package.json used:</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">{</span><br><span class="line"> <span class="string">"name"</span>: <span class="string">"functions"</span>,</span><br><span class="line"> <span class="string">"description"</span>: <span class="string">"Cloud Functions for Firebase"</span>,</span><br><span class="line"> <span class="string">"dependencies"</span>: {</span><br><span class="line"> <span class="comment">// Use for the cloud storage</span></span><br><span class="line"> <span class="string">"@google-cloud/storage"</span>: <span class="string">"^1.4.0"</span>,</span><br><span class="line"> <span class="comment">// A wrapper to use child_process in promise</span></span><br><span class="line"> <span class="string">"child-process-promise"</span>: <span class="string">"^2.2.1"</span>,</span><br><span class="line"> <span class="comment">// Use for manipulating the realtime database</span></span><br><span class="line"> <span class="string">"firebase-admin"</span>: <span class="string">"~5.2.1"</span>,</span><br><span class="line"> <span class="comment">// Mandatory for firebase cloud functions</span></span><br><span class="line"> <span class="string">"firebase-functions"</span>: <span class="string">"^0.6.2"</span>,</span><br><span class="line"> <span class="comment">// A helper library for environement variables</span></span><br><span class="line"> <span class="string">"dotenv"</span>: <span class="string">"^4.0.0"</span>,</span><br><span class="line"> <span class="comment">// A library to manipulate the images (compression, grayscale, ...)</span></span><br><span class="line"> <span class="string">"get-pixels"</span>: <span class="string">"^3.3.0"</span>,</span><br><span class="line"> <span class="comment">// Libraries to talk with google authentication</span></span><br><span class="line"> <span class="string">"google-auth-library"</span>: <span class="string">"^0.11.0"</span>,</span><br><span class="line"> <span class="string">"googleapis"</span>: <span class="string">"^22.2.0"</span></span><br><span class="line"> },</span><br><span class="line"> <span class="string">"private"</span>: <span class="literal">true</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>Let’s have a look at the code of my cloud function</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Method trigger when an image is upload</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="built_in">exports</span>.<span class="property">detectImage</span> = functions.<span class="property">storage</span>.<span class="title function_">object</span>().<span class="title function_">onChange</span>(<span class="function"><span class="params">event</span> =></span> {</span><br><span class="line"> <span class="keyword">const</span> object = event.<span class="property">data</span>; <span class="comment">// The Storage object.</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">const</span> fileBucket = object.<span class="property">bucket</span>; <span class="comment">// The Storage bucket that contains the file.</span></span><br><span class="line"> <span class="keyword">const</span> filePath = object.<span class="property">name</span>; <span class="comment">// File path in the bucket.</span></span><br><span class="line"> <span class="keyword">const</span> contentType = object.<span class="property">contentType</span>; <span class="comment">// File content type.</span></span><br><span class="line"> <span class="keyword">const</span> resourceState = object.<span class="property">resourceState</span>; <span class="comment">// The resourceState is 'exists' or 'not_exists' (for file/folder deletions).</span></span><br><span class="line"> <span class="keyword">const</span> metageneration = object.<span class="property">metageneration</span>; <span class="comment">// Number of times metadata has been generated. New objects have a value of 1.</span></span><br><span class="line"></span><br><span class="line"> ...</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">const</span> userId = path.<span class="title function_">dirname</span>(filePath).<span class="title function_">split</span>(path.<span class="property">sep</span>).<span class="title function_">pop</span>();</span><br><span class="line"> <span class="keyword">const</span> drawId = path.<span class="title function_">basename</span>(filePath, <span class="string">'.jpg'</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> prediction.<span class="title function_">predictPromise</span>(event)</span><br><span class="line"> .<span class="title function_">then</span>(<span class="function">(<span class="params">result</span>) =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'Got result for drawId : '</span> + drawId);</span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">updateTree</span>(userId, drawId, result);</span><br><span class="line"> })</span><br><span class="line"> .<span class="title function_">catch</span>(<span class="function">(<span class="params">err</span>) =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'Error trapped !'</span>);</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">error</span>(err);</span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">updateTree</span>(userId, drawId);</span><br><span class="line"> });;</span><br><span class="line"> } <span class="keyword">catch</span> (e) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'Error trapped by catch !'</span>);</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">error</span>(e);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line">});</span><br></pre></td></tr></table></figure><p>The method <code>prediction</code> calls some Cloud ML APIs, you can find the detail of the implementation here <a href="https://github.com/GDG-Nantes/CountDownDevFest2017/blob/master/functions/prediction.js">prediction.js</a>. After getting the result of the classification, I had to update the drawing (changing its parent in the Tree).</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">updateTree</span>(<span class="params">userId, drawId, result</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =></span> {</span><br><span class="line"> admin.<span class="title function_">database</span>().<span class="title function_">ref</span>(<span class="string">`/drawUpload/<span class="subst">${drawId}</span>`</span>).<span class="title function_">once</span>(<span class="string">'value'</span>, <span class="function">(<span class="params">snapshot</span>) =></span> {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="comment">// prepare to update the tree</span></span><br><span class="line"> <span class="keyword">if</span> (snapshot && snapshot.<span class="title function_">val</span>()) {</span><br><span class="line"> <span class="keyword">let</span> snapshotFb = snapshot.<span class="title function_">val</span>();</span><br><span class="line"> <span class="comment">// Update the drawing with the classifications</span></span><br><span class="line"> snapshotFb.<span class="property">tags</span> = <span class="title function_">extractTags</span>(result);</span><br><span class="line"> <span class="comment">// Add the drawing in a new part of tree</span></span><br><span class="line"> admin.<span class="title function_">database</span>().<span class="title function_">ref</span>(<span class="string">`/draw/<span class="subst">${drawId}</span>`</span>).<span class="title function_">set</span>(snapshotFb)</span><br><span class="line"> <span class="comment">// When it's done, I remove the drawing from it's old path</span></span><br><span class="line"> .<span class="title function_">then</span>(<span class="function">() =></span> admin.<span class="title function_">database</span>().<span class="title function_">ref</span>(<span class="string">`/drawUpload/<span class="subst">${drawId}</span>`</span>).<span class="title function_">remove</span>())</span><br><span class="line"> .<span class="title function_">then</span>(<span class="function">() =></span> {</span><br><span class="line"> <span class="title function_">resolve</span>();</span><br><span class="line"> })</span><br><span class="line"> .<span class="title function_">catch</span>(<span class="function">(<span class="params">reason</span>) =></span> {</span><br><span class="line"> <span class="title function_">reject</span>(reason);</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">catch</span> (e) {</span><br><span class="line"> <span class="title function_">reject</span>(e);</span><br><span class="line"> }</span><br><span class="line"> }, <span class="function">(<span class="params">error</span>) =></span> {</span><br><span class="line"> <span class="title function_">reject</span>(error);</span><br><span class="line"> });</span><br><span class="line"> });</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="My-Conclusion"><a href="#My-Conclusion" class="headerlink" title="My Conclusion"></a>My Conclusion</h3><p>The conclusion of this year was that creating an interactive game is something finally easy if you have the rights tools! Using Firebase was a good idea because I don’t have to manage the following aspects: deployment, installations, load, simple authentication, secure access…</p><p>During a short time (less than 30 min), I got 140 players that created around 250 drawings. It was a huge success for me.</p><h3 id="Code-Demo"><a href="#Code-Demo" class="headerlink" title="Code & Demo"></a>Code & Demo</h3><p>You can find the code here: <a href="https://github.com/GDG-Nantes/CountDownDevFest2017">CountDown DevFest 2017</a>.</p><p>If you want to see what people drew, have a look here <a href="https://devfest-draw.firebaseapp.com/summary.html">Summary of CountDown 2017</a></p><h1 id="What’s-Next"><a href="#What’s-Next" class="headerlink" title="What’s Next"></a>What’s Next</h1><p>See <a href="https://jef.binomed.fr/2018/11/30/2018-11-30-5-years-of-DevFest-CountDown-part3">5 years of DevFest CountDown - Part 3</a></p><!-- Imports to use for interactivité --><script type="text/javascript" src="/assets/js_helper/jef-binomed-helper.js"></script><script type="module" src="/assets/2018-11-countdown/countdown.js"></script>]]></content>
<summary type="html"><p>This article is the second of series ( <a href="https://jef.binomed.fr/2018/11/30/2018-11-30-5-years-of-DevFest-CountDown-part1">5 years </summary>
<category term="Conference" scheme="http://jef.binomed.fr/categories/Conference/"/>
<category term="DevFest" scheme="http://jef.binomed.fr/tags/DevFest/"/>
<category term="Canvas" scheme="http://jef.binomed.fr/tags/Canvas/"/>
</entry>
<entry>
<title>5 years of DevFest CountDown - Part 1</title>
<link href="http://jef.binomed.fr/2018/11/30/2018-11-30-5-years-of-DevFest-CountDown-part1/"/>
<id>http://jef.binomed.fr/2018/11/30/2018-11-30-5-years-of-DevFest-CountDown-part1/</id>
<published>2018-11-30T16:00:00.000Z</published>
<updated>2024-01-26T15:18:47.706Z</updated>
<content type="html"><![CDATA[<p>I organize the <a href="https://devfest.gdgnantes.com/">DevFest Nantes</a> as a volunteer of <a href="https://gdgnantes.com/">GDG Nantes</a> for 7 years already.</p><p>If you don’t know what “DevFest” is, it’s a worldwide event lead by the GDG community. You can have a look here <a href="https://devfest.withgoogle.com/">DevFest With Google</a>.</p><p>Each year we work very hard to provide the best experience for our attendees. We try to make DevFest Nantes the most accessible (only 90€ for 2 days with food, goodies, party, conference, …). And one of the things I’m responsible for is the Countdown! Indeed, if you already go to Google I/O, you know that when you are waiting for the launch of the keynote, Google prepares a nice Countdown animation to let you play with the other attendees! We love so much this experience that we tried to create ours to keep that spirit. In this article, I will try to share those countdown and try to explain to you what I’ve learned with each of them.</p><h1 id="Before-2014"><a href="#Before-2014" class="headerlink" title="Before 2014"></a>Before 2014</h1><div style="text-align:center; width:100%;"> <img src="/assets/2018-11-countdown/original_io_countdown.jpg"></div><p>Before I started to write my own Countdown, I first reused a public countdown created for Google IO 2011: <a href="https://experiments.withgoogle.com/google-io-conference-html5-countdown-finale">HTML5 Countdown Finale</a>. It works pretty well but it was not linked to our graphical Theme. That’s why I started in 2014 to create my own countdown</p><h1 id="2014"><a href="#2014" class="headerlink" title="2014"></a>2014</h1><div style="text-align:center; width:100%;"> <img src="/assets/2018-11-countdown/countdown2014.png" width="600px"></div><h3 id="The-idea"><a href="#The-idea" class="headerlink" title="The idea"></a>The idea</h3><p>Before starting to think about “Interactivity” I had to take time to check that everything works well! That’s also our mantra in DevFest Nantes, always build better over something strong! So for the first Countdown, I started following the KISS approach: No Framework, No modules, just a simple HTML with a little bit of CSS.</p><p>The basic idea of this Countdown was to animate the DevFest Nantes t-shirt.</p><div style="text-align:center; width:100%;"> <img src="/assets/2018-11-countdown/tshirt_2014.png" width="400px"></div><p>This project allowed me to think about the mandatory steps of Countdown :</p><ol><li>Play music until the countdown is over</li><li>Launching a video when the countdown is over</li></ol><p>Behind this 2 steps is hidden a basic complexity. How to check the current time without blocking the UI? How to play both music and video?</p><h3 id="Time-management"><a href="#Time-management" class="headerlink" title="Time management"></a>Time management</h3><p>For the first year, I didn’t use the best solution: <code>setInterval</code> but it’s something that won’t block the UI! Today, I prefer to use <code>requestAnimationFrame</code>. Indeed, one of the most important points is to avoid to blocking the event loop.</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> cibleDate = <span class="title class_">Date</span>.<span class="title function_">parse</span>(<span class="string">'2014-11-07T08:35:00Z'</span>)</span><br><span class="line"><span class="keyword">var</span> cancelInterval = <span class="built_in">setInterval</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> currentTime += <span class="number">100</span>;</span><br><span class="line"> <span class="keyword">var</span> deltaTime = cibleDate - currentTime;</span><br><span class="line"> <span class="keyword">var</span> tmpDate = <span class="keyword">new</span> <span class="title class_">Date</span>(deltaTime);</span><br><span class="line"> min.<span class="property">innerHTML</span> = tmpDate.<span class="title function_">getMinutes</span>() < <span class="number">10</span> ? <span class="string">"0"</span>+tmpDate.<span class="title function_">getMinutes</span>() : tmpDate.<span class="title function_">getMinutes</span>();</span><br><span class="line"> sec.<span class="property">innerHTML</span> = tmpDate.<span class="title function_">getSeconds</span>() < <span class="number">10</span> ? <span class="string">"0"</span>+tmpDate.<span class="title function_">getSeconds</span>() : tmpDate.<span class="title function_">getSeconds</span>();</span><br><span class="line"> <span class="title function_">manageSoundVolume</span>(deltaTime);</span><br><span class="line"> <span class="keyword">if</span> (deltaTime <= <span class="number">0</span> ){</span><br><span class="line"> <span class="title function_">endCountDown</span>();</span><br><span class="line"> }</span><br><span class="line">}, <span class="number">100</span>);</span><br></pre></td></tr></table></figure><h3 id="Playing-a-song"><a href="#Playing-a-song" class="headerlink" title="Playing a song"></a>Playing a song</h3><p>For the second problem, even if there is a <a href="https://developer.mozilla.org/fr/docs/Web/API/Web_Audio_API">Web Audio API</a> a simple <code><audio></code> tag hidden in the page does the job very well! In 2014, there wasn’t the restriction of <a href="https://developers.google.com/web/updates/2017/09/autoplay-policy-changes">autoplay policy</a> so I could easily play a sound with this code :</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// Index of the current song in the playlist</span></span><br><span class="line"><span class="keyword">var</span> indexPlaylist = <span class="number">0</span>;</span><br><span class="line"><span class="comment">// The playlist of songs to play</span></span><br><span class="line"><span class="keyword">var</span> playListSongs = [</span><br><span class="line"> <span class="string">'The_Spin_Wires_-_Blackout_Romeo.mp3'</span></span><br><span class="line">];</span><br><span class="line"></span><br><span class="line"><span class="comment">// Load the song in parameter and play it</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">playSound</span>(<span class="params">url</span>){</span><br><span class="line"> audioElt.<span class="title function_">pause</span>();</span><br><span class="line"> audioElt.<span class="property">src</span> = url;</span><br><span class="line"> audioElt.<span class="title function_">play</span>();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// Skip to the next song (and start from 0 if we overflow the index of array)</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">nextSong</span>(<span class="params"></span>){</span><br><span class="line"> <span class="keyword">try</span>{</span><br><span class="line"> <span class="title function_">playSound</span>(<span class="string">"assets/songs/"</span>+playListSongs[indexPlaylist]);</span><br><span class="line"> indexPlaylist = (indexPlaylist + <span class="number">1</span>) % playListSongs.<span class="property">length</span>;</span><br><span class="line"></span><br><span class="line"> }<span class="keyword">catch</span>(err){</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">error</span>(err);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="Graphical-challenge"><a href="#Graphical-challenge" class="headerlink" title="Graphical challenge"></a>Graphical challenge</h3><p>The only challenge I faced was with the CSS. To play with the good text transformation. So for this HTML:</p><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"conteneur"</span>></span></span><br><span class="line"> <span class="comment"><!-- Minutes Number (Left Column)--></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"jaune_clair min chiffre"</span> <span class="attr">id</span>=<span class="string">"min"</span>></span>00<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="comment"><!-- Minutes Unit (Right Column)--></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"jaune_fonce min unit"</span>></span>m<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="comment"><!-- Seconds Number (Left Column)--></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"jaune_clair sec chiffre"</span> <span class="attr">id</span>=<span class="string">"sec"</span>></span>00<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="comment"><!-- Seconds Unit (Right Column)--></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"jaune_fonce sec unit"</span>></span>s<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">div</span>></span></span><br></pre></td></tr></table></figure><p>You will have this CSS:</p><figure class="highlight scss"><table><tr><td class="code"><pre><span class="line"><span class="comment">// Parent container</span></span><br><span class="line"><span class="selector-class">.conteneur</span>{</span><br><span class="line"> ...</span><br><span class="line"> <span class="attribute">perspective</span> : <span class="number">500px</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// Numbers (left column)</span></span><br><span class="line"><span class="selector-class">.min</span><span class="selector-class">.chiffre</span>{</span><br><span class="line"> <span class="attribute">top</span>: <span class="number">280px</span>;</span><br><span class="line"> <span class="attribute">left</span>: <span class="number">132px</span>;</span><br><span class="line"> <span class="attribute">transform</span> : <span class="built_in">rotateY</span>(<span class="number">73deg</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// Units (right column)</span></span><br><span class="line"><span class="selector-class">.min</span><span class="selector-class">.unit</span>{</span><br><span class="line"> <span class="attribute">top</span>: <span class="number">286px</span>;</span><br><span class="line"> <span class="attribute">left</span>: <span class="number">241px</span>;</span><br><span class="line"> <span class="attribute">transform</span>: <span class="built_in">rotateY</span>(-<span class="number">47deg</span>) <span class="built_in">rotateZ</span>(-<span class="number">11deg</span>) <span class="built_in">rotateX</span>(<span class="number">11deg</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>This gives you this result</p><div id="countdown-2014"> <div class="conteneur"> <img src="/assets/2018-11-countdown/tshirt_2014_final_xl_sans_texte.png" id="main"> <div class="jaune_clair min chiffre" id="min">10</div> <div class="jaune_fonce min unit">m</div> <div class="jaune_clair sec chiffre" id="sec">55</div> <div class="jaune_fonce sec unit">s</div> </div></div><p>And the last thing to do was to animate the text at the bottom of the tower. I used the <code>marquee</code> tag because it’s targeted to animate text in a box! Here, the problem comes to the fact that I have 2 faces in my tower and I want to give the impression that the text “slides” around the tower. To create this effect, I had to use 2 <code>marquee</code> and play a timeout that corresponded to the duration of the move of the text.</p><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag"><<span class="name">marquee</span> <span class="attr">behavior</span>=<span class="string">"scroll"</span> <span class="attr">direction</span>=<span class="string">"right"</span> <span class="attr">class</span>=<span class="string">"marquee_1"</span>></span>DevFest Nantes<span class="tag"></<span class="name">marquee</span>></span></span><br><span class="line"><span class="tag"><<span class="name">marquee</span> <span class="attr">behavior</span>=<span class="string">"scroll"</span> <span class="attr">direction</span>=<span class="string">"right"</span> <span class="attr">class</span>=<span class="string">"marquee_2"</span>></span>DevFest Nantes<span class="tag"></<span class="name">marquee</span>></span></span><br></pre></td></tr></table></figure><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">marquee_1.<span class="property">style</span>.<span class="property">display</span> = <span class="string">'none'</span>;</span><br><span class="line">marquee_2.<span class="property">style</span>.<span class="property">display</span> = <span class="string">'none'</span>;</span><br><span class="line"><span class="comment">// The text "DevFest Nantes" takes 2500ms to go through the marquee</span></span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> marquee_1.<span class="property">style</span>.<span class="property">display</span> = <span class="string">''</span>;</span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> marquee_2.<span class="property">style</span>.<span class="property">display</span> = <span class="string">''</span>;</span><br><span class="line"> }, <span class="number">2500</span>);</span><br><span class="line">}, <span class="number">100</span>);</span><br></pre></td></tr></table></figure><p>Here is the result :</p><div style="text-align:center; width:100%;"> <img src="/assets/2018-11-countdown/countdown-2014-markee.gif" width="600px"></div><p>As you can see, there are lots of things to enhance. But it does the job, a unique countdown linked to DevFest Nantes identity!</p><h3 id="Code-demo"><a href="#Code-demo" class="headerlink" title="Code & demo"></a>Code & demo</h3><p>You can find the code here: <a href="https://github.com/GDG-Nantes/DevFestCountDown-2014">CountDown DevFest 2014</a>.</p><p>If you want to see it in action, have a look here <a href="http://gdg-nantes.github.io/DevFestCountDown-2014/">CountDown 2014</a></p><h1 id="2015"><a href="#2015" class="headerlink" title="2015"></a>2015</h1><div style="text-align:center; width:100%;"> <img src="/assets/2018-11-countdown/countdown2015.png" width="800px"></div><h3 id="The-idea-1"><a href="#The-idea-1" class="headerlink" title="The idea"></a>The idea</h3><p>In 2015, the DevFest Theme was the retroGaming. So again, the countdown was inspired again by the t-shirt :</p><div style="text-align:center; width:100%;"> <img src="/assets/2018-11-countdown/t-shirt_final_2015.png" width="500px"></div><p>The idea was to simulate a real “Space Invaders”. As the t-shirt shows a space invader destroying Google technologies logos. I tried to animate the spaceship in order to destroy the last logo at the last second.</p><h3 id="New-Challenges"><a href="#New-Challenges" class="headerlink" title="New Challenges"></a>New Challenges</h3><p>For this version, I tried to keep it as simple as I could, I just introduced a minor toolchain for compiling my Sass files. The main challenge here was to calculate when to destroy or not a logo and how. For a smooth animation, I used a <strong>Canvas</strong>. As I started to play with canvas, I had to rethink some aspects: Animation, Ressources loading. I reused the work of the previous year for the time management except that I started to use the <code>requestAnimationFrame</code> instead of <code>setTimeout</code>. Indeed, using <code>requestAnimationFrame</code> is something mandatory when you want to create something smooth. To understand why it is important, I encourage you to read more articles about the <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop">Javascript EventLoop</a>.</p><p>Or have a look at this conference from Jake Archibald about event loop:</p><div style="text-align:center; width:100%;"><iframe width="560" height="315" src="https://www.youtube.com/embed/cCOL7MC4Pl0" frameborder="0" allowfullscreen></iframe></div><h3 id="Resources-management"><a href="#Resources-management" class="headerlink" title="Resources management"></a>Resources management</h3><p>When you want to deal with resources in video games, it’s recommended to use ‘sprite’ image. ‘Sprites’ let you download all resources in one time. As HTTP2 is not present everywhere. It’s better to download 1 file than several. The browser cannot open more than 6 (for chrome) separated threads for download, so favor the one big cookie download.</p><div style="text-align:center; width:100%;"> <img src="/assets/2018-11-countdown/logos_2015.svg" width="400px"></div><p>When you deal with ‘sprites’ images, you have to position your cursor when you draw. With this technique, you will minimize the number of assets loaded in memory and gain some time on your first load.</p><div style="text-align:center; width:100%;"> <img src="/assets/2018-11-countdown/drawImage.png" width="400px"></div><p>The main idea is to position your cursor and draw it your context:</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">context = canvas.<span class="title function_">getContext</span>(<span class="string">'2d'</span>);</span><br><span class="line">context.<span class="title function_">drawImage</span>(imgSource <span class="comment">//Source image</span></span><br><span class="line">, sx <span class="comment">//sx clipping of source image</span></span><br><span class="line">, sy <span class="comment">//sy clipping of source image</span></span><br><span class="line">, sw <span class="comment">// swidth clipping of source image</span></span><br><span class="line">, sh <span class="comment">// sheight clipping of source image</span></span><br><span class="line">, dx <span class="comment">// x coordinate of drawing on canvas</span></span><br><span class="line">, dy <span class="comment">// y coordinate of drawing on canvas</span></span><br><span class="line">, dw <span class="comment">// width size of the drawing</span></span><br><span class="line">, dh <span class="comment">// height size of the drawing</span></span><br><span class="line">);</span><br></pre></td></tr></table></figure><p>In my case, I created a map to have a reference for each logo in the sprite. This map allowed me animate each logo and keep a reference of its original position in the sprite :</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">positionLogos = {</span><br><span class="line"> <span class="string">'photos'</span> : {x : <span class="number">400</span>, y :<span class="number">430</span>},</span><br><span class="line"> <span class="string">'gplus'</span> : {x : <span class="number">720</span>, y :<span class="number">440</span>},</span><br><span class="line"> <span class="string">'music'</span> : {x : <span class="number">1010</span>, y :<span class="number">440</span>},</span><br><span class="line"> <span class="string">'maps'</span> : {x : <span class="number">1320</span>, y :<span class="number">440</span>},</span><br><span class="line"> <span class="string">'calendar'</span> : {x : <span class="number">400</span>, y :<span class="number">730</span>},</span><br><span class="line"> <span class="string">'keep'</span> : {x : <span class="number">720</span>, y :<span class="number">730</span>},</span><br><span class="line"> <span class="string">'glass'</span> : {x : <span class="number">1010</span>, y :<span class="number">730</span>},</span><br><span class="line"> <span class="string">'android'</span> : {x : <span class="number">1320</span>, y :<span class="number">720</span>},</span><br><span class="line"> <span class="string">'compute'</span> : {x : <span class="number">400</span>, y :<span class="number">1000</span>},</span><br><span class="line"> <span class="string">'play'</span> : {x : <span class="number">720</span>, y :<span class="number">1000</span>},</span><br><span class="line"> <span class="string">'docs'</span> : {x : <span class="number">1010</span>, y :<span class="number">1000</span>},</span><br><span class="line"> <span class="string">'sheets'</span> : {x : <span class="number">1320</span>, y :<span class="number">1020</span>},</span><br><span class="line"> <span class="string">'draw'</span> : {x : <span class="number">400</span>, y :<span class="number">1280</span>},</span><br><span class="line"> <span class="string">'youtube'</span> : {x : <span class="number">700</span>, y :<span class="number">1280</span>},</span><br><span class="line"> <span class="string">'contacts'</span> : {x : <span class="number">1010</span>, y :<span class="number">1280</span>},</span><br><span class="line"> <span class="string">'chrome'</span> : {x : <span class="number">1320</span>, y :<span class="number">1280</span>},</span><br><span class="line"> <span class="string">'gmail'</span> : {x : <span class="number">400</span>, y :<span class="number">1570</span>},</span><br><span class="line"> <span class="string">'playstore'</span> : {x : <span class="number">720</span>, y :<span class="number">1570</span>},</span><br><span class="line"> <span class="string">'movies'</span> : {x : <span class="number">1010</span>, y :<span class="number">1570</span>},</span><br><span class="line"> <span class="string">'hangout'</span> : {x : <span class="number">1320</span>, y :<span class="number">1570</span>},</span><br><span class="line"> <span class="string">'drive'</span> : {x : <span class="number">400</span>, y :<span class="number">1840</span>},</span><br><span class="line"> <span class="string">'news'</span> : {x : <span class="number">720</span>, y :<span class="number">1840</span>},</span><br><span class="line"> <span class="string">'wallet'</span> : {x : <span class="number">1010</span>, y :<span class="number">1840</span>},</span><br><span class="line"> <span class="string">'devs'</span> : {x : <span class="number">1320</span>, y :<span class="number">1840</span>}</span><br><span class="line">},</span><br></pre></td></tr></table></figure><p>Using a key will let me have a reference of each sprite.</p><h3 id="Animate-the-matrix"><a href="#Animate-the-matrix" class="headerlink" title="Animate the matrix"></a>Animate the matrix</h3><p>What I will display on the screen is a grid of 3 rows of 8 logos. Each logo will have a position in the grid and will move between at maximum from 4 steps in the x axis and 1 step in the y axis.</p><div style="text-align:center; width:100%;"> <img src="/assets/2018-11-countdown/countdown2015_grid.png" width="900px"></div><p>So to play with the grid, I kept a reference of all the logos in a matrix.</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">mapLogos = [</span><br><span class="line"> [</span><br><span class="line"> {<span class="attr">id</span>:<span class="string">'photos'</span>,<span class="attr">pos</span>:{<span class="attr">x</span>:<span class="number">2</span>,<span class="attr">y</span>:<span class="number">0</span>},<span class="attr">index</span>:{<span class="attr">row</span>:<span class="number">0</span>,<span class="attr">col</span>:<span class="number">0</span>}, <span class="attr">visible</span>:<span class="literal">true</span>},</span><br><span class="line"> {<span class="attr">id</span>:<span class="string">'gplus'</span>,<span class="attr">pos</span>:{<span class="attr">x</span>:<span class="number">3</span>,<span class="attr">y</span>:<span class="number">0</span>}, <span class="attr">index</span>: {<span class="attr">row</span>:<span class="number">0</span>,<span class="attr">col</span>:<span class="number">1</span>}, <span class="attr">visible</span>:<span class="literal">true</span>},</span><br><span class="line"> {<span class="attr">id</span>:<span class="string">'music'</span>,<span class="attr">pos</span>:{<span class="attr">x</span>:<span class="number">4</span>,<span class="attr">y</span>:<span class="number">0</span>}, <span class="attr">index</span>: {<span class="attr">row</span>:<span class="number">0</span>,<span class="attr">col</span>:<span class="number">2</span>}, <span class="attr">visible</span>:<span class="literal">true</span>},</span><br><span class="line"> {<span class="attr">id</span>:<span class="string">'maps'</span>,<span class="attr">pos</span>:{<span class="attr">x</span>:<span class="number">5</span>,<span class="attr">y</span>:<span class="number">0</span>}, <span class="attr">index</span>: {<span class="attr">row</span>:<span class="number">0</span>,<span class="attr">col</span>:<span class="number">3</span>}, <span class="attr">visible</span>:<span class="literal">true</span>},</span><br><span class="line"> {<span class="attr">id</span>:<span class="string">'calendar'</span>,<span class="attr">pos</span>:{<span class="attr">x</span>:<span class="number">6</span>,<span class="attr">y</span>:<span class="number">0</span>}, <span class="attr">index</span>: {<span class="attr">row</span>:<span class="number">0</span>,<span class="attr">col</span>:<span class="number">4</span>}, <span class="attr">visible</span>:<span class="literal">true</span>},</span><br><span class="line"> {<span class="attr">id</span>:<span class="string">'keep'</span>,<span class="attr">pos</span>:{<span class="attr">x</span>:<span class="number">7</span>,<span class="attr">y</span>:<span class="number">0</span>}, <span class="attr">index</span>: {<span class="attr">row</span>:<span class="number">0</span>,<span class="attr">col</span>:<span class="number">5</span>}, <span class="attr">visible</span>:<span class="literal">true</span>},</span><br><span class="line"> {<span class="attr">id</span>:<span class="string">'glass'</span>,<span class="attr">pos</span>:{<span class="attr">x</span>:<span class="number">8</span>,<span class="attr">y</span>:<span class="number">0</span>}, <span class="attr">index</span>: {<span class="attr">row</span>:<span class="number">0</span>,<span class="attr">col</span>:<span class="number">6</span>}, <span class="attr">visible</span>:<span class="literal">true</span>},</span><br><span class="line"> {<span class="attr">id</span>:<span class="string">'android'</span>,<span class="attr">pos</span>:{<span class="attr">x</span>:<span class="number">9</span>,<span class="attr">y</span>:<span class="number">0</span>}, <span class="attr">index</span>: {<span class="attr">row</span>:<span class="number">0</span>,<span class="attr">col</span>:<span class="number">7</span>}, <span class="attr">visible</span>:<span class="literal">true</span>},</span><br><span class="line"> ],</span><br><span class="line"> [</span><br><span class="line"> {<span class="attr">id</span>:<span class="string">'compute'</span>,<span class="attr">pos</span>:{<span class="attr">x</span>:<span class="number">2</span>,<span class="attr">y</span>:<span class="number">1</span>}, <span class="attr">index</span>: {<span class="attr">row</span>:<span class="number">1</span>,<span class="attr">col</span>:<span class="number">0</span>}, <span class="attr">visible</span>:<span class="literal">true</span>},</span><br><span class="line"> {<span class="attr">id</span>:<span class="string">'play'</span>,<span class="attr">pos</span>:{<span class="attr">x</span>:<span class="number">3</span>,<span class="attr">y</span>:<span class="number">1</span>}, <span class="attr">index</span>: {<span class="attr">row</span>:<span class="number">1</span>,<span class="attr">col</span>:<span class="number">1</span>}, <span class="attr">visible</span>:<span class="literal">true</span>},</span><br><span class="line"> {<span class="attr">id</span>:<span class="string">'docs'</span>,<span class="attr">pos</span>:{<span class="attr">x</span>:<span class="number">4</span>,<span class="attr">y</span>:<span class="number">1</span>}, <span class="attr">index</span>: {<span class="attr">row</span>:<span class="number">1</span>,<span class="attr">col</span>:<span class="number">2</span>}, <span class="attr">visible</span>:<span class="literal">true</span>},</span><br><span class="line"> {<span class="attr">id</span>:<span class="string">'sheets'</span>,<span class="attr">pos</span>:{<span class="attr">x</span>:<span class="number">5</span>,<span class="attr">y</span>:<span class="number">1</span>}, <span class="attr">index</span>: {<span class="attr">row</span>:<span class="number">1</span>,<span class="attr">col</span>:<span class="number">3</span>}, <span class="attr">visible</span>:<span class="literal">true</span>},</span><br><span class="line"> {<span class="attr">id</span>:<span class="string">'draw'</span>,<span class="attr">pos</span>:{<span class="attr">x</span>:<span class="number">6</span>,<span class="attr">y</span>:<span class="number">1</span>}, <span class="attr">index</span>: {<span class="attr">row</span>:<span class="number">1</span>,<span class="attr">col</span>:<span class="number">4</span>}, <span class="attr">visible</span>:<span class="literal">true</span>},</span><br><span class="line"> {<span class="attr">id</span>:<span class="string">'youtube'</span>,<span class="attr">pos</span>:{<span class="attr">x</span>:<span class="number">7</span>,<span class="attr">y</span>:<span class="number">1</span>}, <span class="attr">index</span>: {<span class="attr">row</span>:<span class="number">1</span>,<span class="attr">col</span>:<span class="number">5</span>}, <span class="attr">visible</span>:<span class="literal">true</span>},</span><br><span class="line"> {<span class="attr">id</span>:<span class="string">'contacts'</span>,<span class="attr">pos</span>:{<span class="attr">x</span>:<span class="number">8</span>,<span class="attr">y</span>:<span class="number">1</span>}, <span class="attr">index</span>: {<span class="attr">row</span>:<span class="number">1</span>,<span class="attr">col</span>:<span class="number">6</span>}, <span class="attr">visible</span>:<span class="literal">true</span>},</span><br><span class="line"> {<span class="attr">id</span>:<span class="string">'chrome'</span>,<span class="attr">pos</span>:{<span class="attr">x</span>:<span class="number">9</span>,<span class="attr">y</span>:<span class="number">1</span>}, <span class="attr">index</span>: {<span class="attr">row</span>:<span class="number">1</span>,<span class="attr">col</span>:<span class="number">7</span>}, <span class="attr">visible</span>:<span class="literal">true</span>},</span><br><span class="line"> ],</span><br><span class="line"> [</span><br><span class="line"> {<span class="attr">id</span>:<span class="string">'gmail'</span>,<span class="attr">pos</span>:{<span class="attr">x</span>:<span class="number">2</span>,<span class="attr">y</span>:<span class="number">2</span>}, <span class="attr">index</span>: {<span class="attr">row</span>:<span class="number">2</span>,<span class="attr">col</span>:<span class="number">0</span>}, <span class="attr">visible</span>:<span class="literal">true</span>},</span><br><span class="line"> {<span class="attr">id</span>:<span class="string">'playstore'</span>,<span class="attr">pos</span>:{<span class="attr">x</span>:<span class="number">3</span>,<span class="attr">y</span>:<span class="number">2</span>}, <span class="attr">index</span>: {<span class="attr">row</span>:<span class="number">2</span>,<span class="attr">col</span>:<span class="number">1</span>}, <span class="attr">visible</span>:<span class="literal">true</span>},</span><br><span class="line"> {<span class="attr">id</span>:<span class="string">'movies'</span>,<span class="attr">pos</span>:{<span class="attr">x</span>:<span class="number">4</span>,<span class="attr">y</span>:<span class="number">2</span>}, <span class="attr">index</span>: {<span class="attr">row</span>:<span class="number">2</span>,<span class="attr">col</span>:<span class="number">2</span>}, <span class="attr">visible</span>:<span class="literal">true</span>},</span><br><span class="line"> {<span class="attr">id</span>:<span class="string">'hangout'</span>,<span class="attr">pos</span>:{<span class="attr">x</span>:<span class="number">5</span>,<span class="attr">y</span>:<span class="number">2</span>}, <span class="attr">index</span>: {<span class="attr">row</span>:<span class="number">2</span>,<span class="attr">col</span>:<span class="number">3</span>}, <span class="attr">visible</span>:<span class="literal">true</span>},</span><br><span class="line"> {<span class="attr">id</span>:<span class="string">'drive'</span>,<span class="attr">pos</span>:{<span class="attr">x</span>:<span class="number">6</span>,<span class="attr">y</span>:<span class="number">2</span>}, <span class="attr">index</span>: {<span class="attr">row</span>:<span class="number">2</span>,<span class="attr">col</span>:<span class="number">4</span>}, <span class="attr">visible</span>:<span class="literal">true</span>},</span><br><span class="line"> {<span class="attr">id</span>:<span class="string">'news'</span>,<span class="attr">pos</span>:{<span class="attr">x</span>:<span class="number">7</span>,<span class="attr">y</span>:<span class="number">2</span>}, <span class="attr">index</span>: {<span class="attr">row</span>:<span class="number">2</span>,<span class="attr">col</span>:<span class="number">5</span>}, <span class="attr">visible</span>:<span class="literal">true</span>},</span><br><span class="line"> {<span class="attr">id</span>:<span class="string">'wallet'</span>,<span class="attr">pos</span>:{<span class="attr">x</span>:<span class="number">8</span>,<span class="attr">y</span>:<span class="number">2</span>}, <span class="attr">index</span>: {<span class="attr">row</span>:<span class="number">2</span>,<span class="attr">col</span>:<span class="number">6</span>}, <span class="attr">visible</span>:<span class="literal">true</span>},</span><br><span class="line"> {<span class="attr">id</span>:<span class="string">'devs'</span>,<span class="attr">pos</span>:{<span class="attr">x</span>:<span class="number">9</span>,<span class="attr">y</span>:<span class="number">2</span>}, <span class="attr">index</span>: {<span class="attr">row</span>:<span class="number">2</span>,<span class="attr">col</span>:<span class="number">7</span>}, <span class="attr">visible</span>:<span class="literal">true</span>},</span><br><span class="line"> ]</span><br><span class="line">],</span><br></pre></td></tr></table></figure><p>A good practice is to separate the data processing and the data rendering!<br>In my case, I just want to render a snapshot of the grid. So when I start the initialization of the countdown, I start several <code>setInterval</code>. Each one has its purpose and will separate the different logics.</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// First I load the 'sprites'</span></span><br><span class="line"><span class="title function_">loadSprites</span>([</span><br><span class="line"> {<span class="attr">title</span>:<span class="string">'logos'</span>, <span class="attr">url</span>: <span class="string">'imgs/logos.svg'</span>},</span><br><span class="line"> {<span class="attr">title</span>:<span class="string">'spaceship'</span>, <span class="attr">url</span>: <span class="string">'imgs/spaceship.png'</span>}</span><br><span class="line">]).<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params"></span>){</span><br><span class="line"> <span class="comment">// When the sprites are load, I start multiples 'intervals'</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// One for the music</span></span><br><span class="line"> <span class="title function_">nextSong</span>();</span><br><span class="line"> <span class="comment">// One that display the grid, the space ship, ... (based on requestAnimationFrameRate)</span></span><br><span class="line"> <span class="title function_">runAnimation</span>();</span><br><span class="line"> <span class="comment">// One that move the logos (every 5 000 ms)</span></span><br><span class="line"> <span class="title function_">processMoveLogos</span>();</span><br><span class="line"> <span class="comment">// One that move the spaceship (every 500ms)</span></span><br><span class="line"> <span class="title function_">processMoveSpaceShip</span>();</span><br><span class="line">});</span><br></pre></td></tr></table></figure><p>As you can see, <code>runAnimation</code> is the only method that renders something on the screen and that’s very important because, I don’t have to do complex calculation, or complex code! In this method, I’m just focused on the rendering.</p><h3 id="The-destruction-of-a-logo"><a href="#The-destruction-of-a-logo" class="headerlink" title="The destruction of a logo"></a>The destruction of a logo</h3><p>One of the challenges was to be sure that all logos would be destroyed at the end of the countdown. I tried to make this as automatic as I can. The first thing that is done when the countdown starts is to calculate a few elements :</p><ul><li>The interval between each destruction : <code>(Now - Final time) / Number of logos</code></li><li>The maximum duration in which a spaceship will be under the current logo</li><li>A random order of destruction that will construct a dynamic stack of destructions orders (one row at a time).</li></ul><p>All those calculations let me have a dynamic countdown that will destroy everything at the right time!</p><h3 id="Code-Demo"><a href="#Code-Demo" class="headerlink" title="Code & Demo"></a>Code & Demo</h3><p>You can find the code here: <a href="https://github.com/GDG-Nantes/CountDownDevFest2015">CountDown DevFest 2015</a>.</p><p>If you want to see it in action, have a look here <a href="http://gdg-nantes.github.io/CountDownDevFest2015/">CountDown 2015</a></p><h1 id="What’s-Next"><a href="#What’s-Next" class="headerlink" title="What’s Next"></a>What’s Next</h1><p>See <a href="https://jef.binomed.fr/2018/11/30/2018-11-30-5-years-of-DevFest-CountDown-part2">5 years of DevFest CountDown - Part 2</a> and <a href="https://jef.binomed.fr/2018/11/30/2018-11-30-5-years-of-DevFest-CountDown-part3">5 years of DevFest CountDown - Part 3</a></p><!-- Imports to use for interactivité --><script type="text/javascript" src="/assets/js_helper/jef-binomed-helper.js"></script><script type="module" src="/assets/2018-11-countdown/countdown.js"></script>]]></content>
<summary type="html"><p>I organize the <a href="https://devfest.gdgnantes.com/">DevFest Nantes</a> as a volunteer of <a href="https://gdgnantes.com/">GDG Nantes<</summary>
<category term="Conference" scheme="http://jef.binomed.fr/categories/Conference/"/>
<category term="DevFest" scheme="http://jef.binomed.fr/tags/DevFest/"/>
<category term="Canvas" scheme="http://jef.binomed.fr/tags/Canvas/"/>
</entry>
<entry>
<title>Résolutions pour 2018</title>
<link href="http://jef.binomed.fr/2018/02/01/2018-02-01-last-french-article/"/>
<id>http://jef.binomed.fr/2018/02/01/2018-02-01-last-french-article/</id>
<published>2018-02-01T07:17:23.000Z</published>
<updated>2024-01-26T15:18:47.706Z</updated>
<content type="html"><![CDATA[<p>Janvier est arrivé et comme tout le monde, j’ai pris un certain nombre de résolutions pour cette nouvelle année. Je me suis poser la question de vers où je voulais aller techniquement et vers quoi je voudrais évoluer dans mon rôle de <a href="https://developers.google.com/experts/">GDE WebTechnology</a> et conférencier. Je profite donc de ce post pour partager avec vous ces réflexions.</p><h1 id="Direction-technique"><a href="#Direction-technique" class="headerlink" title="Direction technique"></a>Direction technique</h1><p>Cette année correspond pour moi à un tournant car j’ai pris fin décembre de nouvelles responsabilités dans la société (<a href="https://www.lucca.fr/">Lucca</a>). Je deviens en effet responsable des composants web de nos produits. Cela veut dire que je vais pouvoir me consacrer pleinement dans la mise en place de Web Components et d’une bibliothèque de composants à destination de développeurs. Je ne manquerais pas de partager ici mes avancées, recherches dans ce domaine.</p><p>Côté veille technologique, je reste toujours autant passionné par le web et ses avancées et j’ai choisi de me concentrer sur la mise en place de projets qui me tiennent à coeur depuis un long moment :</p><ul><li>Mise en place d’une librairie javascript pour faciliter l’utilisation du <a href="https://www.myo.com/">Myo</a> pour des présentations.</li><li>Contribution sur un projet qui sera une sorte de “framework” de présentation.</li><li>Création d’un mécanisme de transcription automatique basé sur du machine learning.</li></ul><p>Étant donné que je vais continuer d’organiser le <a href="https://devfest.gdgnantes.com/">DevFest Nantes</a> je ne sais pas si j’aurais le temps de faire tout ça mais au moins l’intention y est :)</p><h1 id="Direction-Speaker-Blog-…"><a href="#Direction-Speaker-Blog-…" class="headerlink" title="Direction Speaker / Blog / …"></a>Direction Speaker / Blog / …</h1><p>Je souhaite franchir un nouveau cap dans mon “expérience speaker” et je souhaite en effet commencer à donner des conférences en anglais. Cela me permettra de gagner en légitimité. De plus, comme toute la documentation du monde informatique est en anglais, j’ai décidé d’arrêter d’écrire mes articles en français. Ce post est donc mon dernier en français ! De la même façon, afin d’augmenter la visibilité de mes articles, j’ai fait le choix d’adopter l’approche <a href="https://indieweb.org/POSSE">POSSE</a> et donc j’ai commencé à publier mes articles sur <a href="https://medium.com/@jean.francois.garreau">medium</a>. Je vais donc garder ce blog comme source principale et je dupliquerais le contenu dans medium ensuite.</p><p>Voilà vous savez tout !</p>]]></content>
<summary type="html"><p>Janvier est arrivé et comme tout le monde, j’ai pris un certain nombre de résolutions pour cette nouvelle année. Je me suis poser la ques</summary>
<category term="News" scheme="http://jef.binomed.fr/categories/News/"/>
</entry>
<entry>
<title>Chrome Devtools - 5 fonctionnalités à suivre (Septembre 2017)</title>
<link href="http://jef.binomed.fr/2017/09/20/2017-09-20-devtools-en-2017/"/>
<id>http://jef.binomed.fr/2017/09/20/2017-09-20-devtools-en-2017/</id>
<published>2017-09-20T12:34:43.000Z</published>
<updated>2024-01-26T15:18:47.706Z</updated>
<content type="html"><![CDATA[<p>Dans cet article je vais me concentrer sur la console afin de vous partager quelques tips & tricks disponibles dans la console des devtools.</p><h2 id="Selection-du-dom"><a href="#Selection-du-dom" class="headerlink" title="Sélection du dom "></a>Sélection du dom <div chrome="stable"></div></h2><p>Plusieurs “raccourcis” existent afin d’interagir avec la fenêtre d’inspection d’élément</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">> $0 <span class="comment">//permet de récupérer l'élément courant sélectionné</span></span><br><span class="line">> $1 <span class="comment">// récupère le précédent élément sélectionné</span></span><br><span class="line">> $N <span class="comment">// etc</span></span><br><span class="line"></span><br><span class="line">> $(<span class="string">'section'</span>) <span class="comment">// équivalent à document.querySelector('section')</span></span><br><span class="line">> $$(<span class="string">'section'</span>) <span class="comment">// équivalent à document.querySelectorAll('section')</span></span><br><span class="line">> $$(<span class="string">'html/body'</span>) <span class="comment">// retourne un array des éléments matchant le XPath en paramètre</span></span><br><span class="line"></span><br><span class="line">> <span class="title function_">inspect</span>(element) <span class="comment">// Inspecte directement l'élément dans la page</span></span><br></pre></td></tr></table></figure><div style="text-align:center; width:100%;"> <img src="/assets/2017-devtools/selector_inspect.png"></div><p>Voici un exemple d’utilisation des méthodes.</p><div style="text-align:center; width:100%;"> <img src="/assets/2017-devtools/selector_inspect.gif"></div><h2 id="Formater-du-texte"><a href="#Formater-du-texte" class="headerlink" title="Formater du texte "></a>Formater du texte <div chrome="stable"></div></h2><p>Afin d’afficher des données dynamiques (objets, temps, valeurs, …) dans la console, nous avons plusieurs possibilités :</p><h3 id="Utilisation-des-backquotes"><a href="#Utilisation-des-backquotes" class="headerlink" title="Utilisation des backquotes"></a>Utilisation des backquotes</h3><p>Introduites avec ES6, les backquotes sont la solution simple et compatible avec vos projets.</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> uneChaine = <span class="string">'du texte'</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`On peut simplement utiliser les backquotes pour afficher <span class="subst">${uneChaine}</span>`</span>);</span><br><span class="line"><span class="comment">// Affichera 'On peut simplement utiliser les backquotes pour afficher du texte'</span></span><br></pre></td></tr></table></figure><h3 id="Utilisation-des-parametres-de-logs-pour-creer-des-chaines"><a href="#Utilisation-des-parametres-de-logs-pour-creer-des-chaines" class="headerlink" title="Utilisation des paramètres de logs pour créer des chaines"></a>Utilisation des paramètres de logs pour créer des chaines</h3><p>La méthode <code>log</code> prend un nombre infini de paramètres permettant de construire une chaine de caractère. Si l’on ne précise pas de caractères spéciaux, la console va se charger de concaténer les éléments les uns après les autres. Ce qui est intéressant avec cette méthode c’est que des objets complexes apparaîtront directement dans le résultat de la console et seront mis en avant.</p><div style="text-align:center; width:100%;"> <img src="/assets/2017-devtools/console_format_infinite_params.png"></div><h3 id="Utilisation-de-caracteres-speciaux-pour-optimiser-l’affichage"><a href="#Utilisation-de-caracteres-speciaux-pour-optimiser-l’affichage" class="headerlink" title="Utilisation de caractères spéciaux pour optimiser l’affichage"></a>Utilisation de caractères spéciaux pour optimiser l’affichage</h3><p>Si on veut avoir une intégration plus fine avec une chaine de caractère préconstruit, on pourra utiliser les paramètres suivants :</p><ul><li><strong>%s</strong> : Format une chaine de caractères.</li><li><strong>%i</strong> ou %d : Format un entier.</li><li><strong>%f</strong> : Format un nombre flottant.</li><li><strong>%o</strong> : Format un élement du DOM (qui pourra s’ouvrir).</li><li><strong>%O</strong> : Format un objet javascript.</li><li><strong>%c</strong> : Appliquera le style css à la chaine en fonction des propriétés css passées dans le second paramètre.</li></ul><p>Ainsi on peut faire des choses comme ça :</p><div style="text-align:center; width:100%;"> <img src="/assets/2017-devtools/console_format_params.png"></div><h2 id="Mesurer-les-performances"><a href="#Mesurer-les-performances" class="headerlink" title="Mesurer les performances "></a>Mesurer les performances <div chrome="stable"></div></h2><h3 id="Mesure-d’un-temps"><a href="#Mesure-d’un-temps" class="headerlink" title="Mesure d’un temps"></a>Mesure d’un temps</h3><p>La console peut nous aider à mesurer les performances de nos pages à travers des simples instructions.</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="variable language_">console</span>.<span class="title function_">time</span>(<span class="string">'myTimer'</span>); <span class="comment">// Initialise un timer pour le label 'myTimer'</span></span><br><span class="line"><span class="title function_">doSomeStuff</span>();</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">timeEnd</span>(<span class="string">'myTimer'</span>); <span class="comment">// Termine le timer et affiche le temps passé en millisecondes</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// affiche : myTimer : 1125.5554645ms</span></span><br></pre></td></tr></table></figure><p>Si aucun paramètre n’est passé, alors le nom du label est ‘default’.</p><h3 id="Declanchement-du-profiler"><a href="#Declanchement-du-profiler" class="headerlink" title="Déclanchement du profiler"></a>Déclanchement du profiler</h3><p>On peut aussi déclencher le profiler javascript sur des méthodes précises avec le même fonctionnement :</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="variable language_">console</span>.<span class="title function_">profile</span>(<span class="string">'myProfiler'</span>); <span class="comment">// démarre le profiling du code avec le label 'myProfiler'</span></span><br><span class="line"><span class="title function_">longMethodToProfile</span>();</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">profileEnd</span>(<span class="string">'myProfiler'</span>); <span class="comment">// stop le profiling</span></span><br></pre></td></tr></table></figure><p>On retrouve ensuite le profiler dans l’onglet Profile.</p><div style="text-align:center; width:100%;"> <img src="/assets/2017-devtools/console_profiler.gif"></div><h3 id="Mise-en-place-d’un-marqueur-dans-la-timeline"><a href="#Mise-en-place-d’un-marqueur-dans-la-timeline" class="headerlink" title="Mise en place d’un marqueur dans la timeline"></a>Mise en place d’un marqueur dans la timeline</h3><p>Dernier log utile, l’utilisation du mot-clé timestamp qui permet d’afficher un repère dans la timeline javascript. On peut ainsi se repérer dans la timeline grâce à ça :</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="variable language_">console</span>.<span class="title function_">timeStamp</span>(<span class="string">'Adding result'</span>);</span><br></pre></td></tr></table></figure><div style="text-align:center; width:100%;"> <img src="/assets/2017-devtools/track-executions-timestamp2.png"></div><h2 id="Un-autre-affichage-est-possible"><a href="#Un-autre-affichage-est-possible" class="headerlink" title="Un autre affichage est possible "></a>Un autre affichage est possible <div chrome="stable"></div></h2><p>Si tout le monde connaît les niveaux de logs classiques : info, debug, warn, error, log. Peu de gens savent que les devtools proposent des affichages différents afin de faciliter la lecture. En voici quelques-uns :</p><h3 id="console-table"><a href="#console-table" class="headerlink" title="console.table"></a>console.table</h3><p>Cette méthode permet d’afficher de façon lisible les <code>arrays</code> dans la console. On pourra ainsi visualiser simplement un tableau d’objets.</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="variable language_">console</span>.<span class="title function_">table</span>([{<span class="attr">a</span>:<span class="number">1</span>, <span class="attr">b</span>:<span class="number">2</span>, <span class="attr">c</span>:<span class="number">3</span>}, {<span class="attr">a</span>:<span class="string">"foo"</span>, <span class="attr">b</span>:<span class="literal">false</span>, <span class="attr">c</span>:<span class="literal">undefined</span>}]);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">table</span>([[<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>], [<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>]]);</span><br></pre></td></tr></table></figure><p>Donne le résultat suivant :</p><div style="text-align:center; width:100%;"> <img src="/assets/2017-devtools/table-arrays.png"></div><p>On peut même aller plus loin dans ce type de logs avec les classes :</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">Person</span>(<span class="params">firstName, lastName, age</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">firstName</span> = firstName;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">lastName</span> = lastName;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">age</span> = age;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> family = {};</span><br><span class="line">family.<span class="property">mother</span> = <span class="keyword">new</span> <span class="title class_">Person</span>(<span class="string">"Susan"</span>, <span class="string">"Doyle"</span>, <span class="number">32</span>);</span><br><span class="line">family.<span class="property">father</span> = <span class="keyword">new</span> <span class="title class_">Person</span>(<span class="string">"John"</span>, <span class="string">"Doyle"</span>, <span class="number">33</span>);</span><br><span class="line">family.<span class="property">daughter</span> = <span class="keyword">new</span> <span class="title class_">Person</span>(<span class="string">"Lily"</span>, <span class="string">"Doyle"</span>, <span class="number">5</span>);</span><br><span class="line">family.<span class="property">son</span> = <span class="keyword">new</span> <span class="title class_">Person</span>(<span class="string">"Mike"</span>, <span class="string">"Doyle"</span>, <span class="number">8</span>);</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">table</span>(family, [<span class="string">"firstName"</span>, <span class="string">"lastName"</span>, <span class="string">"age"</span>]);</span><br></pre></td></tr></table></figure><p>Donne la sortie suivante :</p><div style="text-align:center; width:100%;"> <img src="/assets/2017-devtools/table-people-objects.png"></div><h3 id="console-group"><a href="#console-group" class="headerlink" title="console.group"></a>console.group</h3><p>Cette méthode permet de regrouper visuellement des logs afin de “contextualiser” ses logs et améliorer leur lisibilité</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="variable language_">console</span>.<span class="title function_">group</span>(<span class="string">'Un label de niveau haut pour identifier le groupe'</span>);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'un log dans le groupe'</span>);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">info</span>(<span class="string">'un deuxième log'</span>);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">groupEnd</span>() <span class="comment">// On termine le bloc</span></span><br></pre></td></tr></table></figure><p>Donne le résultat suivant :</p><div style="text-align:center; width:100%;"> <img src="/assets/2017-devtools/console_group.png"></div><h3 id="Ajouter-les-timestamps"><a href="#Ajouter-les-timestamps" class="headerlink" title="Ajouter les timestamps"></a>Ajouter les timestamps</h3><p>De la même façon, il existe dans les paramètres des devtools la possibilité d’afficher par défaut le timestamp du log.</p><div style="text-align:center; width:100%;"> <img src="/assets/2017-devtools/show-timestamps.png"></div><p>Cela aura pour effet d’enlever le “log stacking” !</p><div style="text-align:center; width:100%;"> <img src="/assets/2017-devtools/timestamped-console.png"></div><h2 id="Contextualiser-ses-logs"><a href="#Contextualiser-ses-logs" class="headerlink" title="Contextualiser ses logs "></a>Contextualiser ses logs <div chrome="62"></div></h2><p>Il est possible depuis un moment d’utiliser des contextes pour logguer ses messages dans des contextes spécifiques (Service worker, iframe, …). Mais depuis la version 62 de Chrome, l’affichage du contexte est grandement simplifié. Ainsi le code suivant pour bénéficier d’un affichage dépendant du contexte de log !</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">(<span class="function">() =></span> {</span><br><span class="line"> <span class="keyword">let</span> logContext = <span class="variable language_">console</span>.<span class="title function_">context</span>(<span class="string">'addContext'</span>);</span><br><span class="line"> <span class="keyword">let</span> perfContext = <span class="variable language_">console</span>.<span class="title function_">context</span>(<span class="string">'perfContext'</span>);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> perfContext.<span class="title function_">info</span>(<span class="string">'Start to measure'</span>);</span><br><span class="line"> perfContext.<span class="title function_">time</span>(<span class="string">'Measure'</span>);</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> count = <span class="number">0</span>; count < <span class="number">100</span>; count++){</span><br><span class="line"> logContext.<span class="title function_">info</span>(<span class="string">'Will log the first count %d'</span>,count);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> perfContext.<span class="title function_">timeEnd</span>(<span class="string">'Measure'</span>);</span><br><span class="line"> perfContext.<span class="title function_">info</span>(<span class="string">'End of measure!'</span>);</span><br><span class="line"></span><br><span class="line">})();</span><br></pre></td></tr></table></figure><div style="text-align:center; width:100%;"> <img src="/assets/2017-devtools/log_contexts.gif"></div><p>Si vous voulez aller encore plus loin dans votre utilisation de la console, je vous conseille d’aller lire la documentation officielle disponible ici : <a href="https://developers.google.com/web/tools/chrome-devtools/console/">https://developers.google.com/web/tools/chrome-devtools/console/</a></p><script type="text/javascript" src="/assets/js_helper/jef-binomed-helper.js"></script><script type="text/javascript" src="/assets/2017-devtools/devtools.js"></script>]]></content>
<summary type="html"><p>Dans cet article je vais me concentrer sur la console afin de vous partager quelques tips &amp; tricks disponibles dans la console des de</summary>
<category term="tips" scheme="http://jef.binomed.fr/categories/tips/"/>
<category term="devtools" scheme="http://jef.binomed.fr/tags/devtools/"/>
</entry>
<entry>
<title>Chrome Devtools - 5 fonctionnalités à suivre (Juin 2017)</title>
<link href="http://jef.binomed.fr/2017/06/21/2017-06-21-devtools-en-2017/"/>
<id>http://jef.binomed.fr/2017/06/21/2017-06-21-devtools-en-2017/</id>
<published>2017-06-21T07:02:18.000Z</published>
<updated>2024-01-26T15:18:47.706Z</updated>
<content type="html"><![CDATA[<h1 id="Snippets"><a href="#Snippets" class="headerlink" title="Snippets "></a>Snippets <div chrome="stable"></div></h1><p>Les snippets sont des programmes javascript que l’on peut exécuter dans le contexte javascript courant. Ces genres de scripts sont très pratiques pour tester des nouvelles fonctionnalités d’Ecmascript. D’autres utilisent aussi les snippets pour obtenir des métriques de son site web.</p><div style="text-align:center; width:100%;"> <img src="/assets/2017-devtools/snippets.png"></div><div style="text-align:center; width:100%;"> <img src="/assets/2017-devtools/snippets.gif"></div><p>À titre personnel, je me sers des snippets dès que j’ai besoin de tester du code javascript, au lieu d’ouvrir un Jsbin ou équivalent, je peux tester mon code. Un autre avantage du snippet est le fait que le contexte d’exécution soit lié au domaine, ceci nous permet par exemple de lancer des requêtes sur des APIs ne permettant pas du CORS tout en restant dans le domaine !</p><h1 id="DOM-Breakpoints"><a href="#DOM-Breakpoints" class="headerlink" title="DOM Breakpoints "></a>DOM Breakpoints <div chrome="stable"></div></h1><p>Le dom breakpoint est très pratique pour identifier d’où provient une modification du DOM. De plus, cela peut s’avérer très utile pour débuguer des popins qui disparaissent quand on perd le focus !</p><div style="text-align:center; width:100%;"> <img src="/assets/2017-devtools/dom_breakpoint.png"></div><div style="text-align:center; width:100%;"> <img src="/assets/2017-devtools/dom_breakpoint.gif"></div><p>On peut donc arrêter le script de la page sur les événements suivants :</p><ul><li>Modification dans l’arbre DOM sous l’élément courant</li><li>Modification des attributs de l’élément courant</li><li>Suppression de l’élément courrant</li></ul><h1 id="Blackbox"><a href="#Blackbox" class="headerlink" title="Blackbox "></a>Blackbox <div chrome="stable"></div></h1><p>Le <em>Blackboxing</em> est une feature qui permet d’ignorer complètement un script de la callstack d’appel. Cela offre donc l’avantage de se concentrer sur le débuggage de nos scripts et on n’est donc pas dépendant de la compréhension du framework que l’on utilise pour coder notre site</p><div style="text-align:center; width:100%;"> <img src="/assets/2017-devtools/blackbox.png"></div><div style="text-align:center; width:100%;"> <img src="/assets/2017-devtools/blackbox.gif"></div><h1 id="Breakpoint-inline"><a href="#Breakpoint-inline" class="headerlink" title="Breakpoint inline "></a>Breakpoint inline <div chrome="stable"></div></h1><p>C’est un nouveau type de breakpoint qui vient d’arriver et qui vous permet d’aller vous positionner dans un contexte de débuggage d’une inline function.</p><p>Avant si vous vouliez débugger et observer le contexte d’une méthode de ce type :</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> array = [<span class="string">'1'</span>,<span class="string">'2'</span>,<span class="string">'3'</span>];</span><br><span class="line"></span><br><span class="line">arrray.<span class="title function_">map</span>(<span class="function"><span class="params">number</span> =></span> +number + <span class="number">1</span>);</span><br></pre></td></tr></table></figure><p>Il fallait faire un truc dans le genre :</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> array = [<span class="string">'1'</span>,<span class="string">'2'</span>,<span class="string">'3'</span>];</span><br><span class="line"></span><br><span class="line">arrray.<span class="title function_">map</span>(<span class="function"><span class="params">number</span> =></span> {</span><br><span class="line"><span class="keyword">return</span> +number + <span class="number">1</span></span><br><span class="line">});</span><br></pre></td></tr></table></figure><p>Ceci cassait en effet notre code, car on était obligé de modifier son code pour le débugger.</p><div style="text-align:center; width:100%;"> <img src="/assets/2017-devtools/inline_breakpoint.png"></div><div style="text-align:center; width:100%;"> <img src="/assets/2017-devtools/inline_breakpoint.gif"></div><h1 id="Conditionnal-breakpoint"><a href="#Conditionnal-breakpoint" class="headerlink" title="Conditionnal breakpoint "></a>Conditionnal breakpoint <div chrome="stable"></div></h1><p>Les breakpoints peuvent être conditionnés et on peut ainsi définir de ne déclencher un breakpoint que si notre contexte est dans un certain état. Cela est très pratique pour débugger des longues listes, on pourra donc cibler les conditions d’arrêt.</p><div style="text-align:center; width:100%;"> <img src="/assets/2017-devtools/conditionnal_breakpoint.png"></div><div style="text-align:center; width:100%;"> <img src="/assets/2017-devtools/conditionnal_breakpoint.gif"></div><script type="text/javascript" src="/assets/js_helper/jef-binomed-helper.js"></script><script type="text/javascript" src="/assets/2017-devtools/devtools.js"></script>]]></content>
<summary type="html"><h1 id="Snippets"><a href="#Snippets" class="headerlink" title="Snippets "></a>Snippets <div chrome="stable"></div></h1><p>Les snippets sont</summary>
<category term="tips" scheme="http://jef.binomed.fr/categories/tips/"/>
<category term="devtools" scheme="http://jef.binomed.fr/tags/devtools/"/>
</entry>
<entry>
<title>Retour Google I/O 2017</title>
<link href="http://jef.binomed.fr/2017/05/26/2017-05-26-Retour-Google-IO-2017/"/>
<id>http://jef.binomed.fr/2017/05/26/2017-05-26-Retour-Google-IO-2017/</id>
<published>2017-05-26T11:59:35.000Z</published>
<updated>2024-01-26T15:18:47.706Z</updated>
<content type="html"><![CDATA[<div style="text-align:center; width:100%;"> <img src="/assets/2017-05-IO/io_shoreline.jpg"></div><p>Cette année encore, j’ai eu la chance d’aller au <a href="https://events.google.com/io/">Google I/O</a> : la grand-messe de Google pendant laquelle sont annoncées toutes les nouveautés sur les produits Google. Plutôt que de revenir sur les annonces publiques qui ont été reprises à de multiples endroits (<a href="https://blog.google/topics/developers/all-io17-announcements/">101 announcements</a>) , je vais m’attarder sur les conférences que je suis allé voir et ce qu’il fallait en retenir.</p><h2 id="L’assistant"><a href="#L’assistant" class="headerlink" title="L’assistant"></a>L’assistant</h2><p>Une des nouveautés cette année, c’était les avancées dans l’application assistant et sur les nouvelles possibilitées qui s’offrent à nous pour développer des actions “Google Assistant”. Non seulement “assistant” va être disponible en dehors de l’application <a href="https://play.google.com/store/apps/details?id=com.google.android.apps.fireball&hl=fr">Google Allo</a> mais en plus, elle arrive sur iOS. Du coup, on va pouvoir coder sur assistant un peu partout !</p><h3 id="Building-Apps-for-the-Google-Assistant"><a href="#Building-Apps-for-the-Google-Assistant" class="headerlink" title="Building Apps for the Google Assistant"></a>Building Apps for the Google Assistant</h3><p>Cette session permet de mieux comprendre le processus de développement pour écrire des applications pour l’assistant. On retiendra entre autres l’apparition d’une “console” de déclaration des “actions” Google. Ils ont aussi présenté comment fonctionnait la nouvelle API de transaction du Google Assistant.</p><br><iframe width="560" height="315" src="https://www.youtube.com/embed/Y26vvxCb3zE" frameborder="0" allowfullscreen></iframe><h3 id="Building-Rich-Cross-Platform-Conversational-UX-with-API-AI"><a href="#Building-Rich-Cross-Platform-Conversational-UX-with-API-AI" class="headerlink" title="Building Rich Cross-Platform Conversational UX with API.AI"></a>Building Rich Cross-Platform Conversational UX with API.AI</h3><p>Cette session très intéressante avait pour objectif de revenir sur les possibilités offertes par le site <a href="https://api.ai/">API.ai</a>. Voici en vrac les choses à retenir de cette session :</p><ul><li>API.ai propose pas loin de 18 langages pour offrir une interface de bot.</li><li>De nouvelles fonctionnalités ont fait leur apparition comme :</li><li>L’apprentissage via l’affichage des réponses non reconnues. Ceci peut être très pratique car il permettra au développeur d’ajuster ses phrases de détection et ainsi de les ajouter à la volée dans son modèle pour compléter une action.</li><li>Analytics for action. Ceci permet d’avoir un mécanisme type Google Analytics basé sur ses actions. On pourra donc voir quelles sont les actions les plus populaires et quel chemin l’utilisateur suit à travers notre bot.</li></ul><br><iframe width="560" height="315" src="https://www.youtube.com/embed/K4v_QnngRdg" frameborder="0" allowfullscreen></iframe><h2 id="Le-web-continue-de-progresser"><a href="#Le-web-continue-de-progresser" class="headerlink" title="Le web continue de progresser"></a>Le web continue de progresser</h2><h3 id="Future-faster-Unlock-the-power-of-Web-Components-with-Polymer"><a href="#Future-faster-Unlock-the-power-of-Web-Components-with-Polymer" class="headerlink" title="Future, faster: Unlock the power of Web Components with Polymer"></a>Future, faster: Unlock the power of Web Components with Polymer</h3><p>Cette session permettait de faire un état des lieux des avancés faites dans le monde des webcomponents et avec Polymer depuis l’année dernière. Première chose à retenir : Le support de plus en plus large des navigateurs vis-à-vis des spécifications enfin figées !</p><div style="text-align:center; width:100%;"> <img src="/assets/2017-05-IO/webcomponent_support.jpg"></div><p>La taille du polyfill a aussi énormément baissé et sa réécriture a permis d’avoir une cohabitation dans les frameworks Javascript améliorée. En effet, il n’est plus nécessaire de faire des hacks pour faire marcher les webcomponents dans vos frameworks !</p><br><iframe width="560" height="315" src="https://www.youtube.com/embed/cuoZenpQveQ" frameborder="0" allowfullscreen></iframe><h3 id="The-Mobile-Web-State-of-the-Union"><a href="#The-Mobile-Web-State-of-the-Union" class="headerlink" title="The Mobile Web: State of the Union"></a>The Mobile Web: State of the Union</h3><p>Cette session est toujours très intéressante car elle permet de mettre en avant des retours d’expériences de grandes sociétés qui ont appliqué les concepts et bonnes pratiques du moment en matière de web. Par exemple, cette année, c’est Twitter qui nous faisait un retour sur leur application PWA <strong>“Twitter Lite”</strong>.</p><p>Ce que j’ai pu retenir de cette session c’était entre autres l’annonce de l’arrivée de “lightHouse” (l’outil de benchmark d’une PWA) directement dans les devtools de chrome et l’apparition d’un nouveau “cli” pour faciliter l’écriture d’application web moderne : <a href="https://developers.google.com/web/tools/workbox/">Workbox</a>.</p><p>L’autre aspect qui m’a plu et surtout conforté dans mon discours c’est le retour de <strong>“OLA”</strong>, une société indienne qui propose un service de partage de trajets en voiture. Ils ont écrit la version PWA de leur application et grace à ça, ils ont pu réengager des utilisateurs qu’ils avaient perdus avec l’application native ! Ceci va exactement dans le même sens que ce que je dis en conférences à savoir : les applications natives ne sont pas mortes et l’objectif des PWA n’est pas de les détruire ! Les PWA vont juste offrir une alternative pour un public spécifique et donc permettre aux sociétés de toucher un plus grand nombre d’utilisateurs !</p><br><iframe width="560" height="315" src="https://www.youtube.com/embed/_ssDaecATCM" frameborder="0" allowfullscreen></iframe><h3 id="Compiling-for-the-Web-with-WebAssembly"><a href="#Compiling-for-the-Web-with-WebAssembly" class="headerlink" title="Compiling for the Web with WebAssembly"></a>Compiling for the Web with WebAssembly</h3><p>Je suis allé avant tout dans cette session pour mieux comprendre ce qu’il se cache derrière cette spécification. Après un rapide retour sur l’historique de WebAssembly (NativeClient, Asm.js, …), le speaker est revenu sur le moment où le code WebAssembly est exécuté par rapport au code Javascript du reste de la page. Ceci nous a permis de comprendre le bénéfice de cette spécification reposant uniquement sur quelques instructions. Nous avons eu droit ensuite une démo de webassembly qui tournait sur tous les navigateurs modernes !</p><p>Ce que j’ai retenu de son utilisation et de son intérêt, c’est que WebAssembly est très très proche du fonctionnement du JNI d’Android. Je n’ai pas encore de uses cases pour lesquels j’aurais besoin d’utiliser webAssembly mais c’est bien de savoir que ça existe et que ça fonctionne !</p><br><iframe width="560" height="315" src="https://www.youtube.com/embed/6v4E6oksar0" frameborder="0" allowfullscreen></iframe><h3 id="Great-Progressive-Web-App-Experiences-with-Angular"><a href="#Great-Progressive-Web-App-Experiences-with-Angular" class="headerlink" title="Great Progressive Web App Experiences with Angular"></a>Great Progressive Web App Experiences with Angular</h3><p>Cette session faisait un tour d’horizon des outils présents dans le “cli” d’Angular pour aider les développeurs à écrire des PWA avec Angular.</p><br><iframe width="560" height="315" src="https://www.youtube.com/embed/C8KcW1Nj3Mw" frameborder="0" allowfullscreen></iframe><h3 id="Web-Performance-Leveraging-the-Metrics-that-Most-Affect-User-Experience"><a href="#Web-Performance-Leveraging-the-Metrics-that-Most-Affect-User-Experience" class="headerlink" title="Web Performance: Leveraging the Metrics that Most Affect User Experience"></a>Web Performance: Leveraging the Metrics that Most Affect User Experience</h3><p>Cette session fait partie des plus intéressantes que j’ai pu voir cette année. L’idée de ce talk était de s’intéresser ce que veut dire “mon site s’affiche lentement”. Annoncer un “load time” de 1.3s ne veut rien dire en fait ! Qu’est-ce qui est pris en compte avec ce chiffre ? Le premier pixel affiché ? Le moment où l’utilisateur peut interagir avec la page ? De la même façon avec une population ne disposant pas des mêmes conditions réseaux, d’un utilisateur à l’autre le temps de réponse va évoluer !</p><p>Bref, les conférenciers proposaient 2 choses :</p><ol><li>S’attarder à étudier des graphes de temps de réponse plutôt qu’un temps absolu. Ils disaient qu’il était aussi pertinent de séparer les résultats obtenus sur mobile et sur ordinateurs !</li><li>Savoir ce que l’on mesure. Ils ont proposé plusieurs unités de temps que l’on peut mesurer en fonction du besoin :</li></ol><ul><li>First Paint : affichage des premiers pixels.</li><li>First Contentful Paint : affichage du premier contenu de mise en forme.</li><li>First Meaningful Paint : affiche du premier contenu pertinent.</li><li>Time to Interactive : temps à partir duquel l’utilisateur peut interagir avec la page.</li></ul><p>Afin d’aider les développeurs à mesurer celà, ils ont présenté un ensemble de nouvelles api qui arrivent dans le web et qui permettront de mesurer efficacement les temps de réponse associés.</p><br><iframe width="560" height="315" src="https://www.youtube.com/embed/6Ljq-Jn-EgU" frameborder="0" allowfullscreen></iframe><h3 id="Developer-Tooling-for-Web-Components"><a href="#Developer-Tooling-for-Web-Components" class="headerlink" title="Developer Tooling for Web Components"></a>Developer Tooling for Web Components</h3><p>Cette session présentait les dernières nouveautés de la ligne de commande “Polymer cli” qui est enfin sortie en version 1.0. Ils ont aussi annoncé la sortie d’un plugin vscode pour écrire plus facilement des composants polymer avec entre autres du linting intégré.</p><p>Pour finir, ils ont annoncé la sortie du projet <a href="https://github.com/Polymer/prpl-server-node">PRPL-Server-Node</a> qui permet de fournir un serveur PRPL ready avec tout ce qui faut pour fournir correctement des ressources en http2 etc.</p><br><iframe width="560" height="315" src="https://www.youtube.com/embed/tKvNeNGmOtU" frameborder="0" allowfullscreen></iframe><h3 id="Building-for-Your-Next-Billion-Users"><a href="#Building-for-Your-Next-Billion-Users" class="headerlink" title="Building for Your Next Billion Users"></a>Building for Your Next Billion Users</h3><p>Cette session permettait de revenir sur les enjeux à prendre en compte quand on conçoit des applications qui doivent fonctionner partout et notamment pour les marchés émergents ! En Inde, il y a actuellement 100 millions de nouveaux utilisateurs tous les ans et Google estime qu’en 2020, il y aura 1 milliard d’utilisateurs indiens ! Ce marché n’est donc pas à sous-estimer et voici en gros les normes à prendre en compte en Inde :</p><ul><li>Offline est l’état par défaut, sinon il faut compter sur de la 2G. Les utilisateurs ont donc tendance à “charger” leur contenu le matin avec leur wifi et ils consultent le contenu offline le reste du temps.</li><li>Ils payent le moindre Méga téléchargé, ils hésitent donc énormément à consulter du contenu s’ils ne sont pas derrière un Wifi.</li><li>Il y a beaucoup de langues différentes en Inde et il est important de leur servire leur contenu dans le dialecte attendu.</li></ul><br><iframe width="560" height="315" src="https://www.youtube.com/embed/wD3rpdiLMyY" frameborder="0" allowfullscreen></iframe><h3 id="Production-Progressive-Web-Apps-With-JavaScript-Frameworks"><a href="#Production-Progressive-Web-Apps-With-JavaScript-Frameworks" class="headerlink" title="Production Progressive Web Apps With JavaScript Frameworks"></a>Production Progressive Web Apps With JavaScript Frameworks</h3><p>Cette session était avant tout une revue des différentes possibilités pour faire des PWA avec les frameworks Javascript modernes. Et la chose la plus importante à retenir était que <strong>TOUS</strong> les frameworks proposent maintenant dans leurs outils de ligne de commande, de quoi générer des PWA ! Il a donc été annoncé la sortie pour React / Preact / Vue.js de ligne de commandes spécifiques pour créer des PWA.</p><p>La norme des prochains projets web est donc bien la PWA :)</p><br><iframe width="560" height="315" src="https://www.youtube.com/embed/aCMbSyngXB4" frameborder="0" allowfullscreen></iframe><h3 id="Using-Web-Components-with-Angular"><a href="#Using-Web-Components-with-Angular" class="headerlink" title="Using Web Components with Angular"></a>Using Web Components with Angular</h3><p>Cette session revenait sur les outils et les bonnes pratiques à mettre en place pour faire cohabiter des webComponents dans une application Angular2. Ce que j’ai retenu c’est qu’utiliser un composant en tant que noeud feuille dans son arbre dom marche très bien. Par contre si l’on veut commencer à l’utiliser au milieu de son arbre ou que l’on veut pouvoir manipuler des objets complexes avec son composant le tout en utilisant des mécanismes type ngModel, alors il faudra prévoir des wrappers angular pour faire fonctionner le tout.</p><br><iframe width="560" height="315" src="https://www.youtube.com/embed/Ucq9F-7Xp8I" frameborder="0" allowfullscreen></iframe><h2 id="Et-pour-aller-encore-plus-loin"><a href="#Et-pour-aller-encore-plus-loin" class="headerlink" title="Et pour aller encore plus loin"></a>Et pour aller encore plus loin</h2><p>Voici la liste des sessions que je n’ai pas eu le temps d’aller voir mais je vais regarder à coup sur :</p><ul><li><a href="https://www.youtube.com/watch?v=assSM3rlvZ8">Polymer: Billions Served; Lessons Learned</a></li><li><a href="https://www.youtube.com/watch?v=m-sCdS0sQO8">Progressive Web Apps: Great Experiences Everywhere</a></li><li><a href="https://www.youtube.com/watch?v=hU89pPBmhds">The Future of Web Payments</a></li><li><a href="https://www.youtube.com/watch?v=fw27RFHP2tc">Defining Multimodal Interactions: One Size Does Not Fit All</a></li><li><a href="https://www.youtube.com/watch?v=0PmWruLLUoE">Finding the Right Voice Interactions for Your App</a></li><li><a href="https://www.youtube.com/watch?v=PjjlwAvV8Jg">DevTools: State of the Union 2017</a></li></ul>]]></content>
<summary type="html"><div style="text-align:center; width:100%;">
<img src="/assets/2017-05-IO/io_shoreline.jpg">
</div>
<p>Cette année encore, j’ai eu la </summary>
<category term="Event" scheme="http://jef.binomed.fr/categories/Event/"/>
<category term="polymer" scheme="http://jef.binomed.fr/tags/polymer/"/>
<category term="pwa" scheme="http://jef.binomed.fr/tags/pwa/"/>
<category term="assistant" scheme="http://jef.binomed.fr/tags/assistant/"/>
</entry>
<entry>
<title>B.O.F. Speaker DevoxxFR 2017</title>
<link href="http://jef.binomed.fr/2017/04/28/2017-04-28-B-O-F-Speaker-DevoxxFR-2017/"/>
<id>http://jef.binomed.fr/2017/04/28/2017-04-28-B-O-F-Speaker-DevoxxFR-2017/</id>
<published>2017-04-28T11:33:28.000Z</published>
<updated>2024-01-26T15:18:47.706Z</updated>
<content type="html"><![CDATA[<p>Voici un retour du B.O.F. (Brid Of Feather) Speaker intitulé “Les speakers anonymes” à <a href="https://devoxx.fr/">DevoxxFR</a> que j’ai co-animé avec <a href="https://twitter.com/Audrey_Neveu">Audrey Neveu</a>, <a href="https://twitter.com/hsablonniere">Hubert Sablonnière</a>, <a href="https://twitter.com/PhilippeAntoine">Philippe Antoine</a> et <a href="https://twitter.com/sebi2706">Sebastien Blanc</a>.</p><p>Pour cette session, nous voulions échanger avec d’autres speakers sur des <em>“Tips & Tricks’</em> que l’on a chacun et que l’on veut partager. Afin d’être sur de traiter les sujets souhaités par l’assistance, nous avons décidé de structurer notre session comme un barcamp : chaque participant a eu la possibilité de proposer 3 sujets ou de voter pour 3 sujets ! Voici donc les sujets retenus :</p><ul><li>Gestion des émotions</li><li>Comment mettre du Fun dans ses présentations</li><li>Préparation et outils</li><li>Tips & Tricks</li></ul><p>Les idées principales ont été notées sur des posts-its, cet article sera donc un peu décousu.</p><h2 id="Gestion-des-emotions"><a href="#Gestion-des-emotions" class="headerlink" title="Gestion des émotions"></a>Gestion des émotions</h2><h3 id="Syndrome-de-l’imposteur"><a href="#Syndrome-de-l’imposteur" class="headerlink" title="Syndrome de l’imposteur"></a>Syndrome de l’imposteur</h3><p>Tout le monde dans la salle disait souffrir du syndrome de l’imposteur. Nous étions d’accord pour dire que ce syndrome était sain pour nous tous car il nous permettait de nous remettre en question régulièrement et de nous dépasser.</p><h3 id="Jouer-un-role-vs-etre-soi-meme"><a href="#Jouer-un-role-vs-etre-soi-meme" class="headerlink" title="Jouer un rôle vs être soi-même"></a>Jouer un rôle vs être soi-même</h3><p>Deux courants se sont opposés :</p><ul><li>Ceux qui “jouent un rôle” : l’idée est de prendre de la distance par rapport à notre présence sur scène. Cela a pour effet que le stress n’est plus porté par nous mais par ce rôle !</li><li>Ceux qui “restent eux-mêmes” : l’idée est, au contraire, de se détendre et de se présenter à l’assistance tels que nous sommes, au naturel et de nous reposer sur cette image. On ne cherche donc pas à jouer un rôle, on est simplement présent et on reste tel quel.</li></ul><h3 id="Pair-speaking"><a href="#Pair-speaking" class="headerlink" title="Pair speaking"></a>Pair speaking</h3><p>Afin de répartir le stress, le “pair speaking” est une bonne chose. D’un côté, il garantit pour l’audience un certain dynamisme, de l’autre, il offre la possibilité aux 2 speakers de se compléter, de partager le savoir. Il n’y a plus le stress d’oublier un point important !</p><h3 id="A-lire-ou-a-faire"><a href="#A-lire-ou-a-faire" class="headerlink" title="À lire ou à faire"></a>À lire ou à faire</h3><p>Un ensemble de méthodes et de livres ont été évoqués pour faciliter la gestion de ses émotions :</p><ul><li><a href="https://g.co/kgs/tDwkuz">Comment dominer le stress</a> - par Dale Carnegie</li><li><a href="https://toastmasters.org/">Toastmasters</a> : il s’agit de meetups pour échanger sur les pratiques de speakers.</li><li>Faire du théâtre d’impro : cela permet d’apprendre à maitriser son stress et les cas non prévus (erreur de démo, …)</li><li>Pratiquer, pratiquer, pratiquer. D’une manière générale, rien ne remplace la pratique. Il est conseillé de pratiquer régulièrement et de continuer à faire évoluer ses processus, ses thèmes, …</li></ul><div style="text-align:center; width:100%;"> <img src="/assets/2017-04-BofSpeaker/emotions.jpg"></div><h2 id="Comment-mettre-du-Fun-dans-ses-presentations"><a href="#Comment-mettre-du-Fun-dans-ses-presentations" class="headerlink" title="Comment mettre du Fun dans ses présentations"></a>Comment mettre du Fun dans ses présentations</h2><p>Afin d’amener du fun, plusieurs éléments ont été envisagés :</p><ul><li>Ramener un tshirt “fun” avec un lien avec la présentation.</li><li>Faire jouer l’audience.</li><li>Ramener des cadeaux pour les participants.</li><li>Faire un peu de mise en scène à travers des “personnages”.</li></ul><h3 id="Gifs-or-not-Gifs"><a href="#Gifs-or-not-Gifs" class="headerlink" title="Gifs or not Gifs ?"></a>Gifs or not Gifs ?</h3><p>Le débat sur l’utilisation des Gifs a été lancé mais au final tout le monde était d’accord pour dire que les gifs ne doivent pas être utilisés si un contenu important est affiché à l’écran. Les Gifs peuvent être affichés avec du contenu uniquement si les propos suivent l’animation. Et dans tous les cas, il ne faut pas laisser l’animation tourner en boucle car cela captera inutilement l’attention de vos participants.</p><div style="text-align:center; width:100%;"> <img src="/assets/2017-04-BofSpeaker/fun.jpg"></div><h2 id="Preparation-et-outils"><a href="#Preparation-et-outils" class="headerlink" title="Préparation et outils"></a>Préparation et outils</h2><h3 id="Preparation-du-plan-et-des-idees"><a href="#Preparation-du-plan-et-des-idees" class="headerlink" title="Préparation du plan et des idées"></a>Préparation du plan et des idées</h3><p>Plusieurs techniques sont remontées concernant la préparation du plan :</p><ul><li>Tout dans la tête : certains gardent tout en tête et ne posent pas à un endroit leurs idées. Tout est écrit directement.</li><li>Mindmap : certains commencent par une Mindmap afin de poser l’ensemble des idées importantes du talk. C’est ensuite un point de départ pour structurer ses idées et commencer son plan.</li><li>Tableau blanc : certains posent pendant plusieurs jours sur un tableau blanc le plan et les idées.</li><li>D’autres vont créer leur plan en fonction de leurs démos.</li></ul><h3 id="Repetitions"><a href="#Repetitions" class="headerlink" title="Répétitions"></a>Répétitions</h3><p>De la même façon, plusieurs techniques sont ressorties concernant les répétitions :</p><ul><li>Tester ses démos en offline : L’idée étant que l’on ne peut jamais faire confiance dans le réseau et il convient donc vérifier que tout peut être montré même si on n’a pas de réseau.</li><li>Répéter devant des personnes de confiance afin d’avoir des feedbacks pertinents. L’important est d’avoir des personnes qui peuvent nous donner un retour objectif et des axes d’amélioration.</li><li>Répéter à blanc et/ou s’enregistrer. Cela permet de vérifier que le temps de présentation correspond au temps prévu et aussi de tester l’enchainement des slides.</li><li>Dans la même veine, certains apprennent par coeur leur introduction afin de se lancer facilement dans le flow de leur conférence.</li></ul><div style="text-align:center; width:100%;"> <img src="/assets/2017-04-BofSpeaker/prepa_outils.jpg"></div><h2 id="Tips-Tricks"><a href="#Tips-Tricks" class="headerlink" title="Tips & Tricks"></a>Tips & Tricks</h2><h3 id="Git-Branches"><a href="#Git-Branches" class="headerlink" title="Git & Branches"></a>Git & Branches</h3><p>Afin de switcher facilement entre 2 démos et/ou entre 2 états de codes instables lors d’un live coding, certains utilisent Git avec un mécanisme de branche. Cela leur permet de restaurer rapidement du code propre et d’éviter le stress associé à une parenthèse manquante !</p><p>D’autres utilisent des diffs Git couplés avec une configuration de leur IDE ce qui leur permet d’avoir des aides de codes directement dans leur IDE.</p><h3 id="Enchainement-“Ludique”"><a href="#Enchainement-“Ludique”" class="headerlink" title="Enchainement “Ludique”"></a>Enchainement “Ludique”</h3><p>Afin de ne pas endormir l’audience, il est important de travailler une suite “logique” et cohérente qui a pour objectif d’emmener les participants avec soi.</p><h3 id="Utilisation-de-videos-pour-les-demos"><a href="#Utilisation-de-videos-pour-les-demos" class="headerlink" title="Utilisation de vidéos pour les démos"></a>Utilisation de vidéos pour les démos</h3><p>Afin d’éviter l’effet démo, certains enregistrent toutes leurs démos en “videocast” afin de commenter la vidéo. Cela montre quand même la fonctionnalité et permet d’éviter les problèmes.</p><h3 id="Bosser-la-conclusion"><a href="#Bosser-la-conclusion" class="headerlink" title="Bosser la conclusion"></a>Bosser la conclusion</h3><p>Il est important de travailler une conclusion à son talk afin de faire comprendre aux participants que votre sujet est clos. L’idée principale étant d’éviter le “voilà voilà !” ou autres.</p><h3 id="Notes-pendant-la-presentation"><a href="#Notes-pendant-la-presentation" class="headerlink" title="Notes pendant la présentation"></a>Notes pendant la présentation</h3><p>Plusieurs speakers avaient des notes speakers afin de garder le fil et de s’assurer un backup. La plupart du temps ces notes ne sont pas lues mais elles permettent de se rassurer. Tout le monde était par contre d’accord sur un point : ces notes ne devaient en aucun cas être lues sur scène !</p><h3 id="Des-ronds-pour-indiquer-l’avancement"><a href="#Des-ronds-pour-indiquer-l’avancement" class="headerlink" title="Des ronds pour indiquer l’avancement"></a>Des ronds pour indiquer l’avancement</h3><p>Certains mettent en place des petites pastilles de couleurs dans leurs slides pour leur indiquer leur avancement. Ces pastilles sont très peu visibles par le spectateur mais le speaker le sait et il peut savoir où il en est par rapport au temps qu’il lui reste.</p><h3 id="Poser-des-questions"><a href="#Poser-des-questions" class="headerlink" title="Poser des questions"></a>Poser des questions</h3><p>D’autres speakers posent tout un ensemble de questions au début du talk afin de juger le niveau des participants. Par exemple, on commence par une question “Qui est développeur ?” et petit à petit, on précise nos questions afin de réduire le nombre de mains levées. De cette manière, on peut savoir quel est le niveau de l’audience.</p><div style="text-align:center; width:100%;"> <img src="/assets/2017-04-BofSpeaker/tips_tricks.jpg"></div><h2 id="En-conclusion"><a href="#En-conclusion" class="headerlink" title="En conclusion"></a>En conclusion</h2><p>Merci encore à la team avec laquelle j’ai eu la chance d’organiser ce BOF. Les échanges étaient vraiment pertinents et intéressants. N’hésitez pas à les suivre si ce n’est pas déjà le cas car ils ont vraiment du contenu intéressant à partager régulièrement !</p><div style="text-align:center; width:100%;"> <img src="/assets/2017-04-BofSpeaker/team_bof.jpg"></div>]]></content>
<summary type="html"><p>Voici un retour du B.O.F. (Brid Of Feather) Speaker intitulé “Les speakers anonymes” à <a href="https://devoxx.fr/">DevoxxFR</a> que j’ai</summary>
<category term="Tips" scheme="http://jef.binomed.fr/categories/Tips/"/>
<category term="tips" scheme="http://jef.binomed.fr/tags/tips/"/>
<category term="speaker" scheme="http://jef.binomed.fr/tags/speaker/"/>
</entry>
<entry>
<title>Chrome Devtools - 5 fonctionnalités à suivre (Mars 2017)</title>
<link href="http://jef.binomed.fr/2017/03/31/2017-03-31-devtools-en-2017/"/>
<id>http://jef.binomed.fr/2017/03/31/2017-03-31-devtools-en-2017/</id>
<published>2017-03-31T07:37:04.000Z</published>
<updated>2024-01-26T15:18:47.705Z</updated>
<content type="html"><![CDATA[<p><strong>Avant propos</strong></p><p>Je lance une nouvelle série d’articles que je vais compléter régulièrement dont l’objectif principal est de présenter des fonctionnalités des Chrome Devtools. L’idée n’est donc pas de sortir un “Top 5” car il y a trop de fonctionnalités et la notion de “Top” est beaucoup trop subjective… Je vais donc me contenter de présenter des fonctionnalités.</p><p>Beaucoup des fonctionnalités que je vais présenter ne sont disponibles qu’à travers les “experiments” des devtools. Je tacherais de le préciser si c’est le cas.</p><p>Pour activer les devtools expérimentaux, il suffit de se rendre sur le lien suivant : <a href="chrome://flags/#enable-devtools-experiments">#enable-devtools-experiments</a> et d’activer la fonctionnalité souhaitée dans le menu suivant (accessible dans les settings)</p><div style="text-align:center; width:100%;"> <img src="/assets/2017-devtools/activate_experiment.png"></div><h1 id="Raccourcis-claviers"><a href="#Raccourcis-claviers" class="headerlink" title="Raccourcis claviers "></a>Raccourcis claviers <div chrome='stable'></div></h1><p>Les devtools proposent tout un ensemble de raccourcis claviers qui vont vous permettre de contrôler l’intégralité des actions disponibles dans les devtools</p><div style="text-align:center; width:100%;"> <img src="/assets/2017-devtools/shortcut.png"></div><p>Cette liste est non exhaustive et vous pouvez retrouver la liste complète des raccourcis à cette url : <a href="https://developers.google.com/web/tools/chrome-devtools/iterate/inspect-styles/shortcuts">Devtools Shortcut List</a></p><p>Même si cette liste vous parraît grande, il y a un raccourci à retenir plus que tous les autres : <code>CTRL + SHIFT + P</code> ou <code>CMD + SHIFT + P</code> ce dernier vous donner accès à l’ensemble des fonctionnalités des devtools ! </p><div style="text-align:center; width:100%;"> <img src="/assets/2017-devtools/ultime_shortcut.png"></div><h1 id="Changer-de-theme"><a href="#Changer-de-theme" class="headerlink" title="Changer de thème "></a>Changer de thème <div chrome='stable'></div></h1><p>Aujourd’hui, de plus en plus d’IDE proposent des thèmes “dark” et bien les devtools ne dérogent pas à la règle et vous proposent un thème sombre. Pour l’activer, soit vous passez par le raccourci “ultime”, soit vous passez par le menu de paramètres.</p><div style="text-align:center; width:100%;"> <img src="/assets/2017-devtools/theme_menu.png"></div><div style="text-align:center; width:100%;"> <img src="/assets/2017-devtools/theme_shortcut.png"></div><p>Ce qui vous donnera ça : </p><div style="text-align:center; width:100%;"> <img src="/assets/2017-devtools/theme_dark.png"></div><h1 id="Workspace-avec-persistance-2-0"><a href="#Workspace-avec-persistance-2-0" class="headerlink" title="Workspace avec persistance 2.0 "></a>Workspace avec persistance 2.0 <i class="fa fa-flask"></i> <div chrome='stable'></div></h1><div style="text-align:center; width:100%;"> <img src="/assets/2017-devtools/persistance_activate.png"></div><p>Cette fonctionnalité permet de révolutionner le fonctionnement des <strong>“workspaces”</strong> qui pour rappel, lie un répertoire de votre ordinateur avec les fichiers de votre site (si un sourcemap est mis en place). Cette version 2.0 permet donc de glisser déposer un répertoire système vers les devtools et le mapping se fait <strong>“automatiquement” !</strong> </p><div style="text-align:center; width:100%;"> <img src="/assets/2017-devtools/workspace.gif"></div><p>Pour vérifier que le mapping a été effectué correctement, il suffit de regarder s’il y a un petit point vert </p><div style="text-align:center; width:100%;"> <img src="/assets/2017-devtools/persistance_2.0.png"></div><p>Vous pourrez ensuite éditer vos fichiers directement dans les devtools</p><div style="text-align:center; width:100%;"> <img src="/assets/2017-devtools/workspace_2.gif"></div><h1 id="Smart-Console"><a href="#Smart-Console" class="headerlink" title="Smart Console "></a>Smart Console <div chrome="stable"></div></h1><p>La smart console est une fonctionnalité intéressante qui vous permet de saisir du code dans votre console sans avoir à se préoccuper d’écrire une fonction <strong>“inline”</strong></p><p>On pourra donc se retrouver à écrire des fonctions dans la console comme suit : </p><div style="text-align:center; width:100%;"> <img src="/assets/2017-devtools/smart_console.png"></div><div style="text-align:center; width:100%;"> <img src="/assets/2017-devtools/smart_console.gif"></div><h1 id="Quick-Source"><a href="#Quick-Source" class="headerlink" title="Quick Source "></a>Quick Source <div chrome="stable"></div></h1><p>La fenêtre “Quick Source” est très pratique car elle permet de pointer dans le panel d’inspection l’emplacement dans le fichier source correspondant ! En pratique ça veut dire quoi ? Que quand je suis en train de finaliser / tester des styles, je sais exactement où éditer mon fichier et je peux le faire directement depuis les devtools, ce qui m’évite d’avoir à switcher entre mes fenêtres.</p><p>Pour accéder à la fenêtre quick source, il faut aller dans le bas des devtools</p><div style="text-align:center; width:100%;"> <img src="/assets/2017-devtools/quick_source_activate.png"></div><p>Un nouvel onglet apparaît ensuite dans le bas de la page : </p><div style="text-align:center; width:100%;"> <img src="/assets/2017-devtools/panel_quick_source.png"></div><p>Si l’on clique sur une propriété CSS de l’élément inspecté, alors la fenêtre quick source se synchronise directement.</p><div style="text-align:center; width:100%;"> <img src="/assets/2017-devtools/quick_source_in_action.png"></div><div style="text-align:center; width:100%;"> <img src="/assets/2017-devtools/quicksource.gif"></div>## Pour conclure sur cet article<p>Je sais que j’ai oublié un grand nombre de fonctionnalités mais, mon idée est de sortir plus régulièrement ce type d’articles pour compléter au fil de l’eau des fonctionnalités disponibles dans les devtools.</p><script type="text/javascript" src="/assets/js_helper/jef-binomed-helper.js"></script><script type="text/javascript" src="/assets/2017-devtools/devtools.js"></script>]]></content>
<summary type="html"><p><strong>Avant propos</strong></p>
<p>Je lance une nouvelle série d’articles que je vais compléter régulièrement dont l’objectif principal</summary>
<category term="tips" scheme="http://jef.binomed.fr/categories/tips/"/>
<category term="devtools" scheme="http://jef.binomed.fr/tags/devtools/"/>
</entry>
<entry>
<title>J'ai mis en place les RTTs le vendredi ;)</title>
<link href="http://jef.binomed.fr/2017/02/06/2017-02-06-j-ai-mis-en-place-les-RTT-le-vendredi/"/>
<id>http://jef.binomed.fr/2017/02/06/2017-02-06-j-ai-mis-en-place-les-RTT-le-vendredi/</id>
<published>2017-02-06T20:45:04.000Z</published>
<updated>2024-01-26T15:18:47.705Z</updated>
<content type="html"><![CDATA[<h2 id="Avant-propos"><a href="#Avant-propos" class="headerlink" title="Avant propos"></a>Avant propos</h2><p>Ceux qui me connaissent savent que j’aime partager en donnant des conférences. Mais j’aime aussi emmener des gens avec moi dans cette aventure. Il y a de ça 1 an et demi, j’ai créé un cursus pour aider des gens à franchir le pas et ainsi devenir speakers(euses). Si le sujet vous intéresse, je vous invite à lire mes 3 articles qui traitent du sujet : </p><ol><li><a href="http://jef.binomed.fr/2016/03/11/2016-03-11-comment-devenir-speaker-part-1/">Comment trouver son sujet</a></li><li><a href="https://jef.binomed.fr/2016/04/06/2016-04-06-comment-devenir-speaker-part-2/">Où aller et comment se comporter sur scène</a></li><li><a href="https://jef.binomed.fr/2016/05/02/2016-05-02-comment-devenir-speaker-part-3/">Comment gérer sa carrière de speaker(euse) ?</a></li></ol><p>Mais ce n’est pas le sujet de cet article. Je souhaite m’attarder sur une nouvelle initiative que j’ai lancée au sein de ma société : <a href="https://www.lucca.fr/">Lucca</a> afin d’aider les gens dans l’animation de conférences. </p><h2 id="Le-concept-RTT"><a href="#Le-concept-RTT" class="headerlink" title="Le concept : RTT"></a>Le concept : RTT</h2><p>J’ai lancé les RTT : <strong>R</strong>abbi<strong>T</strong> <strong>T</strong>alk </p><p>Tous les vendredis à 11h45, une personne va intervenir pendant 15 minutes sur le sujet de son choix devant ses collègues. Une fois l’intervention terminée, il (elle) désignera son successeur qui devra donner le prochain RTT.</p><h3 id="Pourquoi-RTT-pour-RabbiT-Talk"><a href="#Pourquoi-RTT-pour-RabbiT-Talk" class="headerlink" title="Pourquoi RTT pour RabbiT Talk ?"></a>Pourquoi RTT pour RabbiT Talk ?</h3><p>À l’origine, c’était les Rabbit Tech Talk mais l’aspect Tech pouvait être trop réducteur, on a donc gardé le deuxième <strong>“T”</strong> de rabbi<strong>T</strong>. Rabbit car ces sessions sont très rapides ! </p><p>Pour finir, RTT pour une société dont l’un des softs principaux s’occupe de congés, c’est quand même logique ;) !</p><h2 id="Les-enjeux"><a href="#Les-enjeux" class="headerlink" title="Les enjeux"></a>Les enjeux</h2><p>Ils sont multiples : </p><ul><li>Partager régulièrement la connaissance sans pour autant assommer les gens avec un format long.</li><li>Identifier des sujets pouvant être partagés à plus grande échelle.</li><li>Permettre à des personnes de s’initier à l’art de parler en public.</li></ul><p>Le fait de désigner le successeur a pour objectif d’inciter les gens à prendre la parole. Fonctionner avec un mécanisme de bénévolat montrerait trop vite ses limites et ce seraient toujours les mêmes qui prendraient la parole. Désigner forcera un peu les collaborateurs à sortir de leurs zones de confort tout en restant dans un environnement connu et bienveillant.</p><h2 id="Les-contraintes"><a href="#Les-contraintes" class="headerlink" title="Les contraintes"></a>Les contraintes</h2><p>Afin de garantir le succès de l’initiative, nous avons contraint un certain nombre de choses pour s’assurer du bon fonctionnement des RTTs : </p><h3 id="Decision-collegiale"><a href="#Decision-collegiale" class="headerlink" title="Décision collégiale"></a>Décision collégiale</h3><p>La mise en place des RTTs a été approuvé à l’unanimité par l’équipe. En effet, je voulais m’assurer de l’adhésion de l’équipe car cela reste un peu contraignant pour celui qui doit préparer le RTT.</p><h3 id="15-minutes-sans-interruption"><a href="#15-minutes-sans-interruption" class="headerlink" title="15 minutes sans interruption"></a>15 minutes sans interruption</h3><p>La conférence ne doit pas durer plus de 15 minutes car cela permet de se concentrer sur un seul point. </p><p>D’un côté, cela facilite la préparation. On ne doit pas passer 1 journée à préparer son RTT. Le RTT n’a pas pour vocation à montrer un travail abouti graphiquement mais juste de s’occuper de présenter un sujet.</p><p>L’aspect sans interruption est important car 15 minutes, c’est très court ! Il est donc primordial que l’auditoire soit discipliné. Si on a une question, on la note et on attend que l’intervenant(e) ait fini sa présentation. Cela est d’une part plus respectueux et d’autre part, ça peut être l’occasion d’alimenter des discussions à la pause-déjeuner ;)</p><p>À chaque séance, une personne fait le <em>“time-keeper”</em> pour s’assurer du bon respect du temps.</p><h3 id="11h45-et-pas-avant"><a href="#11h45-et-pas-avant" class="headerlink" title="11h45 et pas avant"></a>11h45 et pas avant</h3><p>Le fait de choisir 11h45 est lié au timing, en effet, à cette heure ci, les gens ont en général faim et personne n’a envie de s’éterniser avant d’aller manger. L’heure et le timing du RTT sont donc importants car cela nous force à être concis et donc à aller droit au but !</p><h3 id="Le-format-et-le-sujet-sont-libres"><a href="#Le-format-et-le-sujet-sont-libres" class="headerlink" title="Le format et le sujet sont libres"></a>Le format et le sujet sont libres</h3><p>Au sein de Lucca, comme dans beaucoup de boîtes, tous les profils se côtoient au jour le jour et le RTT ne doit pas être un moment d’exclusion de certains profils.<br>Pour cette raison, le RTT n’est pas forcément technique. Il peut l’être mais ce n’est pas un prérequis !</p><p>Voici quelques exemples de sujets que nous avons : </p><ul><li>Réécrivons l’histoire avec git : exemple d’utilisation de commandes Git</li><li>La conjugaison des verbes au participe passé.</li><li>Ce qu’il faut dire ou ne pas dire en public.</li></ul><p>Bref, comme le sujet est libre, le RTT ne doit pas être obligatoirement une présentation. Du live-coding, une démonstration d’outils peuvent aussi convenir ! </p><p>Le RTT est un moment de partage et cet échange peut revêtir la forme que l’on souhaite ;)</p><h3 id="L’evenement-doit-etre-recurrent-et-regulier"><a href="#L’evenement-doit-etre-recurrent-et-regulier" class="headerlink" title="L’événement doit être récurrent et régulier !"></a>L’événement doit être récurrent et régulier !</h3><p>Il est important de se forcer à garder le rythme ! Introduire des pauses dans une initiative comme celle-ci, c’est s’avouer vaincu et c’est garantir la mort du concept assez rapidement. </p><p>Le principal est de se mettre d’accord avec ses collègues avant de lancer la chose.</p><h3 id="Il-n’y-a-aucune-obligation-a-faire-un-RTT"><a href="#Il-n’y-a-aucune-obligation-a-faire-un-RTT" class="headerlink" title="Il n’y a aucune obligation à faire un RTT !"></a>Il n’y a aucune obligation à faire un RTT !</h3><p>Bien que la prochaine personne à animer le RTT soit désignée, il est très important de ne pas forcer les gens ! L’un des enjeux principaux du RTT est d’inciter, pas de contraindre. Si pour une raison X ou Y, une personne ne peut pas assurer le prochain RTT, elle a le droit de refuser. La raison du refus n’a pas besoin d’être justifiée non plus. Cependant, il est important que la personne choisie donne sa réponse pour le lundi suivant maximum. Il faut éviter au maximum de se retrouver la veille à trouver un sujet !</p><p>Comme il n’y a aucune obligation, il n’est pas interdit de demander à l’audience si quelqu’un veut être le suivant. Bien sûr, ce n’est pas l’idée originale mais cela peut permettre aussi à certains qui ont des sujets déjà prêts d’accélérer le partage.</p><h3 id="Il-faut-un-totem"><a href="#Il-faut-un-totem" class="headerlink" title="Il faut un totem"></a>Il faut un totem</h3><p>Nous avons utilisé un “totem” pour indiquer qui doit effectuer le prochain RTT. En plus d’être un indicateur visuel, il concrétise en quelque sorte un rituel de passation :)</p><div style="text-align:center; width:100%;"> <img src="/assets/2017-02-RTT/totem.jpg"></div> <h2 id="Premiers-retours"><a href="#Premiers-retours" class="headerlink" title="Premiers retours"></a>Premiers retours</h2><p>Même si cela ne fait pas très longtemps que le concept est lancé, le format plaît beaucoup et les spectateurs comme les conférenciers(ières) sont tous très contents d’avoir pu s’essayer à ça et sont contents d’avoir pu partager un sujet qui les intéressait !</p><p>Je vous encourage donc à reproduire l’expérience chez vous ;)</p>]]></content>
<summary type="html"><h2 id="Avant-propos"><a href="#Avant-propos" class="headerlink" title="Avant propos"></a>Avant propos</h2><p>Ceux qui me connaissent savent</summary>
<category term="Tips" scheme="http://jef.binomed.fr/categories/Tips/"/>
<category term="tips" scheme="http://jef.binomed.fr/tags/tips/"/>
<category term="speaker" scheme="http://jef.binomed.fr/tags/speaker/"/>
</entry>
<entry>
<title>J'ai fait un jeux multijoueur en temps réel server-less et offline (Partie 3 - Partie graphique)</title>
<link href="http://jef.binomed.fr/2016/12/23/2016-12-23-legonnary-3/"/>
<id>http://jef.binomed.fr/2016/12/23/2016-12-23-legonnary-3/</id>
<published>2016-12-23T16:05:46.000Z</published>
<updated>2024-01-26T15:18:47.705Z</updated>
<content type="html"><![CDATA[<p><img src="/assets/2016-12-legonnary/devfest_photo.jpg"></p><p>Troisième partie d’une série de 3 articles : </p><ul><li><a href="/2016/12/23/2016-12-23-legonnary/">Article 1 - PWA</a></li><li><a href="/2016/12/23/2016-12-23-legonnary-2/">Article 2 - Firebase</a></li></ul><h2 id="Chalenges-graphiques"><a href="#Chalenges-graphiques" class="headerlink" title="Chalenges graphiques"></a>Chalenges graphiques</h2><p>Pour ce projet, j’ai dû faire face à 2 chalenges techniques : </p><ol><li>Reproduire une grille Lego ©.</li><li>Afficher les dessins à l’écran principal avec une animation simulant un flash suivit d’une image type polaroid.</li></ol><h3 id="Grille-Lego-©"><a href="#Grille-Lego-©" class="headerlink" title="Grille Lego ©"></a>Grille Lego ©</h3><p>Finalement, faire la grille Lego © a été quelque chose de plutôt simple. Grâce à la libraire <a href="http://fabricjs.com/">FrabricJS</a>, j’ai pu reproduire un pion vu de haut. Une brique est donc l’addition d’un carré avec une ombre sur lequel, j’ai posé 2 cercles avec un jeu de couleurs et d’ombres. Enfin, je rajoute le mot “GDG” à la Place de Lego © et le tour est joué !</p><p><strong>Étape 1 : le carré avec l’ombre</strong></p><div style="text-align:center; width:100%;"> <img src="/assets/2016-12-legonnary/brique_1.png"></div> <figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="variable language_">this</span>.<span class="property">rectBasic</span> = <span class="keyword">new</span> fabric.<span class="title class_">Rect</span>({</span><br><span class="line"> <span class="attr">width</span>: cellSize * size.<span class="property">col</span>,</span><br><span class="line"> <span class="attr">height</span>: cellSize * size.<span class="property">row</span>,</span><br><span class="line"> <span class="attr">fill</span>: color,</span><br><span class="line"> <span class="attr">originX</span>: <span class="string">'center'</span>,</span><br><span class="line"> <span class="attr">originY</span>: <span class="string">'center'</span>,</span><br><span class="line"> <span class="attr">centeredRotation</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="attr">hasControls</span>: <span class="literal">false</span>,</span><br><span class="line"> shadow : <span class="string">"5px 5px 10px rgba(0,0,0,0.2)"</span> </span><br><span class="line">});</span><br></pre></td></tr></table></figure><p><strong>Étape 2 : le premier cercle (cercle extérieur)</strong></p><div style="text-align:center; width:100%;"> <img src="/assets/2016-12-legonnary/brique_2.png"></div> <figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">new</span> fabric.<span class="title class_">Circle</span>({</span><br><span class="line"> <span class="attr">radius</span>: (cellSize / <span class="number">2</span>) - <span class="number">4</span>,</span><br><span class="line"> <span class="attr">fill</span>: <span class="title class_">ColorLuminance</span>(color, <span class="number">0.1</span>),</span><br><span class="line"> <span class="attr">originX</span>: <span class="string">'center'</span>,</span><br><span class="line"> <span class="attr">originY</span>: <span class="string">'center'</span></span><br><span class="line">});</span><br></pre></td></tr></table></figure><p><strong>Étape 3 : le second cercle avec une ombre</strong></p><div style="text-align:center; width:100%;"> <img src="/assets/2016-12-legonnary/brique_3.png"></div> <figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">new</span> fabric.<span class="title class_">Circle</span>({</span><br><span class="line"> <span class="attr">radius</span>: (cellSize / <span class="number">2</span>) - <span class="number">5</span>,</span><br><span class="line"> <span class="attr">fill</span>: <span class="title class_">ColorLuminance</span>(color, -<span class="number">0.1</span>),</span><br><span class="line"> <span class="attr">originX</span>: <span class="string">'center'</span>,</span><br><span class="line"> <span class="attr">originY</span>: <span class="string">'center'</span>,</span><br><span class="line"> shadow : <span class="string">"0px 2px 10px rgba(0,0,0,0.2)"</span></span><br><span class="line">});</span><br></pre></td></tr></table></figure><p><strong>Étape 4 : L’ajout du texte GDG</strong></p><div style="text-align:center; width:100%;"> <img src="/assets/2016-12-legonnary/brique_4.png"></div> <figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">new</span> fabric.<span class="title class_">Text</span>(<span class="string">'GDG'</span>, {</span><br><span class="line"> <span class="attr">fontSize</span>: cellSize / <span class="number">5</span>,</span><br><span class="line"> <span class="attr">fill</span>: <span class="title class_">ColorLuminance</span>(color, -<span class="number">0.15</span>),</span><br><span class="line"> <span class="attr">originX</span>: <span class="string">'center'</span>,</span><br><span class="line"> <span class="attr">originY</span>: <span class="string">'center'</span>,</span><br><span class="line"> <span class="attr">stroke</span>: <span class="title class_">ColorLuminance</span>(color, -<span class="number">0.20</span>),</span><br><span class="line"> <span class="attr">strokeWidth</span>: <span class="number">1</span></span><br><span class="line">});</span><br></pre></td></tr></table></figure><p>Même si cela n’est pas parfait, à une plus petite échelle, cela permet d’avoir un effet plutôt bon ! </p><p>Le code correspondant est au niveau des fichiers <a href="https://github.com/GDG-Nantes/CountDownDevFest2016/blob/master/src/scripts/lego_shape/peg.js">peg.js</a> et <a href="https://github.com/GDG-Nantes/CountDownDevFest2016/blob/master/src/scripts/lego_shape/circle.js">circle.js</a></p><h3 id="Animations-CSS"><a href="#Animations-CSS" class="headerlink" title="Animations CSS"></a>Animations CSS</h3><p>L’animation CSS est décomposée en plusieurs animations : </p><ol><li>Un flash.</li><li>L’affichage de mon image sous forme de photo polaroid.</li><li>Le déplacement de mon image dans un coin de mon écran.</li></ol><p><strong>Le Flash</strong></p><div id="parent-flash"> <div id="flash-effect" class="flash"></div></div><p>Pour réussir cette partie, c’est très simple, il suffit de jouer avec une div blanche avec un dégradé vers de la transparence et il suffit d’afficher cette div pour la faire disparaitre très rapidement.</p><figure class="highlight css"><table><tr><td class="code"><pre><span class="line"><span class="selector-id">#flash-effect</span>{</span><br><span class="line"> <span class="attribute">position</span>:absolute; </span><br><span class="line"> <span class="attribute">width</span>:<span class="number">500px</span>;</span><br><span class="line"> <span class="attribute">height</span>:<span class="number">500px</span>;</span><br><span class="line"> <span class="attribute">background</span>:<span class="built_in">radial-gradient</span>(ellipse at center, <span class="built_in">rgba</span>(<span class="number">255</span>,<span class="number">255</span>,<span class="number">255</span>,<span class="number">1</span>) <span class="number">0%</span>, <span class="built_in">rgba</span>(<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>) <span class="number">80%</span>);</span><br><span class="line"> <span class="attribute">opacity</span>:<span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#flash-effect</span><span class="selector-class">.flash</span>{</span><br><span class="line"> <span class="attribute">animation</span>: flash;</span><br><span class="line"> <span class="attribute">animation-duration</span>: <span class="number">1s</span>; </span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">@keyframes</span> flash {</span><br><span class="line"> <span class="selector-tag">from</span> {<span class="attribute">opacity</span>:<span class="number">1</span>;}</span><br><span class="line"> <span class="selector-tag">to</span> {<span class="attribute">opacity</span>: <span class="number">0</span>}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>Ainsi le fait d’appliquer la classe <code>flash</code> créera automatiquement cet effet de flash photo.</p><p><strong>L’effet Polaroid</strong></p><div id="parent-negatif"> <div class="img-ori-parent big" data-author="jefBinomed"> <img class="img-ori" src="/assets/2016-12-legonnary/gdg_logo_legonnary.png" > </div></div><p>Pour faire cet effet avec un nombre minimum d’éléments, j’ai simplement joué avec les before / after et sur les attributs HTML exploitables en CSS.</p><p>Ainsi voici le code HTML : </p><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"img-ori-parent"</span> <span class="attr">data-author</span>=<span class="string">"jefBinomed"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">img</span> <span class="attr">class</span>=<span class="string">"img-ori"</span> <span class="attr">src</span>=<span class="string">"/assets/2016-12-legonnary/gdg_logo_legonnary.png"</span> ></span></span><br><span class="line"><span class="tag"></<span class="name">div</span>></span></span><br></pre></td></tr></table></figure><p>et voici le code CSS correspondant : </p><figure class="highlight css"><table><tr><td class="code"><pre><span class="line"><span class="selector-class">.img-ori-parent</span>{</span><br><span class="line"> <span class="attribute">position</span>:absolute;</span><br><span class="line"> <span class="attribute">width</span>:<span class="built_in">calc</span>(<span class="number">200px</span> + <span class="number">40px</span>);</span><br><span class="line"> <span class="attribute">height</span>: <span class="built_in">calc</span>(<span class="number">200px</span> + <span class="number">100px</span>);</span><br><span class="line"> <span class="attribute">background</span>:white;</span><br><span class="line"> <span class="attribute">z-index</span>: <span class="number">10</span>;</span><br><span class="line"> <span class="attribute">box-shadow</span> : <span class="number">0px</span> <span class="number">0px</span> <span class="number">5px</span> <span class="number">0px</span> <span class="built_in">rgba</span>(<span class="number">50</span>, <span class="number">50</span>, <span class="number">50</span>, <span class="number">0.75</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.img-ori</span>{</span><br><span class="line"> </span><br><span class="line"> <span class="attribute">position</span>:absolute;</span><br><span class="line"> <span class="attribute">top</span>:<span class="number">20px</span>;</span><br><span class="line"> <span class="attribute">left</span>:<span class="number">20px</span>;</span><br><span class="line"> <span class="attribute">width</span>:<span class="number">200px</span>;</span><br><span class="line"> <span class="attribute">height</span>:<span class="number">200px</span>;</span><br><span class="line"> <span class="attribute">background-size</span>: contain;</span><br><span class="line"> <span class="attribute">background-repeat</span>: no-repeat;</span><br><span class="line"> <span class="attribute">box-shadow</span> : <span class="number">0px</span> <span class="number">0px</span> <span class="number">5px</span> <span class="number">0px</span> <span class="built_in">rgba</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">1.5</span>) inset;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.img-ori-parent</span><span class="selector-pseudo">::after</span>{</span><br><span class="line"> <span class="attribute">content</span>:<span class="built_in">attr</span>(data-author);</span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="attribute">width</span>:<span class="number">100%</span>;</span><br><span class="line"> <span class="attribute">text-align</span>:center;</span><br><span class="line"> <span class="attribute">bottom</span>: <span class="number">15pt</span>;</span><br><span class="line"> <span class="attribute">left</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">font-size</span>:<span class="number">20pt</span>;</span><br><span class="line"> <span class="attribute">line-height</span>:<span class="number">20pt</span>;</span><br><span class="line"> <span class="attribute">font-family</span>:<span class="string">"Roboto"</span>,<span class="string">"Helvetica"</span>,<span class="string">"Arial"</span>,sans-serif;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>De cette manière, on peut voir qu’avec simplement un jeu d’ombres, de after, before, on peut donner un effet Polaroid à une image ! </p><p><strong>L’animation de rétrécissement</strong></p><div id="parent-negatif"> <div class="img-ori-parent big anim" data-author="jefBinomed"> <img class="img-ori" src="/assets/2016-12-legonnary/gdg_logo_legonnary.png" > </div></div><p>Cette animation est assurée par la propriété transition. On écoute ainsi toutes les évolutions de tailles, positions, … et on déclenche une transition de façon à rendre ça plus fluide.</p><p>La raison pour laquelle je ne passe pas par la propriété <code>animation</code> de CSS, est que la position d’arrivée sera complètement aléatoire ! En effet, une fois l’image apparue, on va la positionner dans l’écran de façon aléatoire. Une fois à gauche et une fois à droite. Sa position horizontale et verticale sera certes bornée, mais le résultat sera issu d’un <code>Math.random()</code>. Donc en utilisant la propriété <code>transition</code> plutôt que <code>animation</code>, je peux m’assurer qu’il y aura une animation fluide et prenant en compte tous les cas. </p><p>Dans mon animation, j’ai géré 2 états : </p><ol><li>Le parent a la classe <code>.big</code> : Dans cet état, l’image est grande et positionnée au centre de l’écran</li><li>Le parent n’a plus la classe <code>.big</code> : Dans cet état, l’image va prendre une taille plus réduite. Les top & left seront fixés directement par le Javascript</li></ol><p>Voici le code CSS à produire pour gérer simplement l’animation :</p><figure class="highlight scss"><figcaption><span>screen.scss</span><a href="https://github.com/GDG-Nantes/CountDownDevFest2016/blob/master/src/sass/screen.scss#L53">link</a></figcaption><table><tr><td class="code"><pre><span class="line"><span class="variable">$size-photo</span>: <span class="number">200px</span>;</span><br><span class="line"><span class="variable">$size-photo-big</span>: <span class="number">500px</span>;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.img-ori-parent</span><span class="selector-class">.big</span>{</span><br><span class="line"> <span class="attribute">width</span>:<span class="variable">$size-photo-big</span>+<span class="number">90px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="variable">$size-photo-big</span>+<span class="number">200px</span>;</span><br><span class="line"> <span class="attribute">background</span>:white;</span><br><span class="line"> <span class="attribute">box-shadow</span> : <span class="number">0px</span> <span class="number">0px</span> <span class="number">5px</span> <span class="number">0px</span> <span class="built_in">rgba</span>(<span class="number">50</span>, <span class="number">50</span>, <span class="number">50</span>, <span class="number">0.75</span>); </span><br><span class="line"></span><br><span class="line"> <span class="selector-class">.img-ori</span>{ </span><br><span class="line"> <span class="attribute">top</span>:<span class="number">45px</span>;</span><br><span class="line"> <span class="attribute">left</span>:<span class="number">45px</span>;</span><br><span class="line"> <span class="attribute">width</span>:<span class="variable">$size-photo-big</span>;</span><br><span class="line"> <span class="attribute">height</span>:<span class="variable">$size-photo-big</span>; </span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.img-ori-parent</span><span class="selector-class">.big</span><span class="selector-pseudo">::after</span>{</span><br><span class="line"> <span class="attribute">bottom</span>: <span class="number">15pt</span>;</span><br><span class="line"> <span class="attribute">left</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">font-size</span>:<span class="number">50pt</span>;</span><br><span class="line"> <span class="attribute">line-height</span>:<span class="number">50pt</span>;</span><br><span class="line"> <span class="attribute">transition-property</span>: all;</span><br><span class="line"> <span class="attribute">transition-duration</span>: <span class="number">2s</span>;</span><br><span class="line"> <span class="attribute">transition</span>-timing-fonction: ease;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="selector-class">.img-ori-parent</span>{</span><br><span class="line"> <span class="attribute">position</span>:absolute;</span><br><span class="line"> <span class="attribute">width</span>:<span class="variable">$size-photo</span>+<span class="number">40px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="variable">$size-photo</span>+<span class="number">100px</span>;</span><br><span class="line"> <span class="attribute">background</span>:white;</span><br><span class="line"> <span class="attribute">z-index</span>: <span class="number">10</span>;</span><br><span class="line"> <span class="attribute">box-shadow</span> : <span class="number">0px</span> <span class="number">0px</span> <span class="number">5px</span> <span class="number">0px</span> <span class="built_in">rgba</span>(<span class="number">50</span>, <span class="number">50</span>, <span class="number">50</span>, <span class="number">0.75</span>);</span><br><span class="line"> <span class="attribute">transition-property</span>: all;</span><br><span class="line"> <span class="attribute">transition-duration</span>: <span class="number">2s</span>;</span><br><span class="line"> <span class="attribute">transition</span>-timing-fonction: ease;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.img-ori</span>{</span><br><span class="line"> </span><br><span class="line"> <span class="attribute">position</span>:absolute;</span><br><span class="line"> <span class="attribute">top</span>:<span class="number">20px</span>;</span><br><span class="line"> <span class="attribute">left</span>:<span class="number">20px</span>;</span><br><span class="line"></span><br><span class="line"> <span class="attribute">width</span>:<span class="variable">$size-photo</span>;</span><br><span class="line"> <span class="attribute">height</span>:<span class="variable">$size-photo</span>;</span><br><span class="line"> <span class="attribute">background-size</span>: contain;</span><br><span class="line"> <span class="attribute">background-repeat</span>: no-repeat;</span><br><span class="line"> <span class="attribute">box-shadow</span> : <span class="number">0px</span> <span class="number">0px</span> <span class="number">5px</span> <span class="number">0px</span> <span class="built_in">rgba</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">1.5</span>) inset;</span><br><span class="line"> <span class="attribute">transition-property</span>: all;</span><br><span class="line"> <span class="attribute">transition-duration</span>: <span class="number">2s</span>;</span><br><span class="line"> <span class="attribute">transition</span>-timing-fonction: ease;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.img-ori-parent</span><span class="selector-pseudo">::after</span>{</span><br><span class="line"> <span class="attribute">content</span>:<span class="built_in">attr</span>(data-author);</span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="attribute">width</span>:<span class="number">100%</span>;</span><br><span class="line"> <span class="attribute">text-align</span>:center;</span><br><span class="line"> <span class="attribute">bottom</span>: <span class="number">15pt</span>;</span><br><span class="line"> <span class="attribute">left</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">font-size</span>:<span class="number">20pt</span>;</span><br><span class="line"> <span class="attribute">line-height</span>:<span class="number">20pt</span>;</span><br><span class="line"> <span class="attribute">font-family</span>:<span class="string">"Roboto"</span>,<span class="string">"Helvetica"</span>,<span class="string">"Arial"</span>,sans-serif;</span><br><span class="line"> <span class="attribute">transition-property</span>: all;</span><br><span class="line"> <span class="attribute">transition-duration</span>: <span class="number">2s</span>;</span><br><span class="line"> <span class="attribute">transition</span>-timing-fonction: linear;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>Voici ensuite comment avec le Javascript j’anime le tout : </p><ol><li>Je déclenche le “flash”.</li><li>Après un léger timeout (le temps du flash), je créé une nouvelle DIV avec la classe <code>.big</code>.</li><li>Après un deuxième timeout (le temps de laisser le dessin à l’écran pour les participants), je supprime la classe <code>.big</code> et je donne des valeurs aléatoires au <code>top</code> & <code>left</code> de la DIV parente.</li></ol><figure class="highlight javascript"><figcaption><span>app_screen.js</span><a href="https://github.com/GDG-Nantes/CountDownDevFest2016/blob/master/src/scripts/app_screen.js#L35">link</a></figcaption><table><tr><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">generateSnapshot</span>(<span class="params">user, dataUrl</span>) {</span><br><span class="line"> <span class="comment">// We start our flash effect</span></span><br><span class="line"> <span class="keyword">let</span> rectCanvas = <span class="variable language_">document</span>.<span class="title function_">querySelector</span>(<span class="string">'.canvas-container'</span>).<span class="title function_">getBoundingClientRect</span>();</span><br><span class="line"> <span class="keyword">let</span> flashDiv = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">'flash-effect'</span>)</span><br><span class="line"> flashDiv.<span class="property">style</span>.<span class="property">top</span> = (rectCanvas.<span class="property">top</span> - <span class="number">250</span>) + <span class="string">"px"</span>;</span><br><span class="line"> flashDiv.<span class="property">style</span>.<span class="property">left</span> = (rectCanvas.<span class="property">left</span> - <span class="number">250</span>) + <span class="string">"px"</span>;</span><br><span class="line"> flashDiv.<span class="property">classList</span>.<span class="title function_">add</span>(<span class="string">'flash'</span>);</span><br><span class="line"> <span class="comment">//When the animation is done (1s of opacity .7 -> 0 => ~500ms to wait)</span></span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="function">() =></span> {</span><br><span class="line"> <span class="comment">// We create the final image</span></span><br><span class="line"> <span class="comment">// We create a div that we will be animate</span></span><br><span class="line"> flashDiv.<span class="property">classList</span>.<span class="title function_">remove</span>(<span class="string">'flash'</span>);</span><br><span class="line"> <span class="keyword">let</span> imgParent = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">'div'</span>);</span><br><span class="line"> <span class="keyword">let</span> img = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">'img'</span>);</span><br><span class="line"> img.<span class="property">src</span> = dataUrl;</span><br><span class="line"> img.<span class="property">classList</span>.<span class="title function_">add</span>(<span class="string">'img-ori'</span>);</span><br><span class="line"> imgParent.<span class="property">classList</span>.<span class="title function_">add</span>(<span class="string">'img-ori-parent'</span>);</span><br><span class="line"> imgParent.<span class="title function_">setAttribute</span>(<span class="string">'data-author'</span>, user);</span><br><span class="line"> imgParent.<span class="title function_">appendChild</span>(img);</span><br><span class="line"> imgParent.<span class="property">classList</span>.<span class="title function_">add</span>(<span class="string">'big'</span>);</span><br><span class="line"> <span class="comment">// Initial Position</span></span><br><span class="line"> imgParent.<span class="property">style</span>.<span class="property">top</span> = (rectCanvas.<span class="property">top</span> - <span class="number">45</span>) + <span class="string">"px"</span>;</span><br><span class="line"> imgParent.<span class="property">style</span>.<span class="property">left</span> = (rectCanvas.<span class="property">left</span> - <span class="number">45</span>) + <span class="string">"px"</span>;</span><br><span class="line"></span><br><span class="line"> <span class="variable language_">document</span>.<span class="property">body</span>.<span class="title function_">appendChild</span>(imgParent);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// we wait a litle to set new position to the new div. The css animation will do the rest of the job</span></span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> horizontalDist = <span class="title class_">Math</span>.<span class="title function_">floor</span>(<span class="title class_">Math</span>.<span class="title function_">random</span>() * <span class="number">300</span>) + <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">let</span> heightScreen = <span class="variable language_">document</span>.<span class="property">body</span>.<span class="title function_">getBoundingClientRect</span>().<span class="property">height</span>;</span><br><span class="line"> <span class="keyword">let</span> verticalDist = <span class="title class_">Math</span>.<span class="title function_">floor</span>(<span class="title class_">Math</span>.<span class="title function_">random</span>() * (heightScreen - <span class="number">100</span> - <span class="number">300</span>)) + <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">let</span> angleChoice = <span class="title class_">Math</span>.<span class="title function_">floor</span>(<span class="title class_">Math</span>.<span class="title function_">random</span>() * <span class="number">3</span>) + <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"> imgParent.<span class="property">classList</span>.<span class="title function_">remove</span>(<span class="string">'big'</span>);</span><br><span class="line"> imgParent.<span class="property">style</span>.<span class="property">top</span> = <span class="string">`calc(100px + <span class="subst">${verticalDist}</span>px)`</span>;</span><br><span class="line"> imgParent.<span class="property">style</span>.<span class="property">left</span> = <span class="string">`<span class="subst">${horizontalDist}</span>px`</span>;</span><br><span class="line"> <span class="keyword">if</span> (!lastLeft) { <span class="comment">// True if the last photo was placed at the left of the countDown</span></span><br><span class="line"> imgParent.<span class="property">style</span>.<span class="property">left</span> = <span class="string">`calc(100vw - <span class="subst">${horizontalDist}</span>px - 300px)`</span>; <span class="comment">// The timeout date </span></span><br><span class="line"> }</span><br><span class="line"> lastLeft = !lastLeft; <span class="comment">// True if the last photo was placed at the left of the countDown</span></span><br><span class="line"> <span class="keyword">let</span> angle = angleChoice === <span class="number">1</span> ? -<span class="number">9</span> : angleChoice === <span class="number">2</span> ? <span class="number">14</span> : <span class="number">0</span>; <span class="comment">// The timeout date</span></span><br><span class="line"> imgParent.<span class="property">style</span>.<span class="property">transform</span> = <span class="string">`rotate(<span class="subst">${angle}</span>deg)`</span>;</span><br><span class="line"> <span class="title function_">getNextDraw</span>();</span><br><span class="line"> }, <span class="number">100</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// When the element is create, we clean the board</span></span><br><span class="line"> legoCanvas.<span class="title function_">resetBoard</span>();</span><br><span class="line"> <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">'proposition-text'</span>).<span class="property">innerHTML</span> = <span class="string">"En attente de proposition"</span>;</span><br><span class="line"></span><br><span class="line"> }, <span class="number">500</span>);</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure><p><strong>Le résultat final</strong></p><div id="parent-final"> <img class="img-ori empty" src="/assets/2016-12-legonnary/empty_legonnary.png"> <img class="img-ori temp" src="/assets/2016-12-legonnary/gdg_logo_legonnary.png" > <div class="img-ori-parent big final-anim" data-author="jefBinomed"> <img class="img-ori" src="/assets/2016-12-legonnary/gdg_logo_legonnary.png" > </div> <div class="flash-final flash"></div></div><h2 id="Tout-ce-dont-je-n’ai-pas-parle"><a href="#Tout-ce-dont-je-n’ai-pas-parle" class="headerlink" title="Tout ce dont je n’ai pas parlé"></a>Tout ce dont je n’ai pas parlé</h2><p>Il reste encore beaucoup de points non abordés et je veux bien répondre aux questions si vous en avez : </p><ul><li>La gestion du compte à rebours.</li><li>La gestion de l’audio & vidéo.</li></ul><p>En conclusion, j’ai encore appris pas mal de choses avec ce projet et si je devais revoir certaines parties, je pense que je ferais les choix suivants : </p><ul><li>J’essayerais d’utiliser une autre librairie JS de canvas, D3 ? Car même, si FabricJS est facile d’accès et fait bien le job, j’ai constaté quelques soucis avec le touch et quelques problèmes de performances liés à la librairie sur certains téléphones.</li><li>Je tâcherais d’alléger un peu plus mon arbre firebase, notamment sur le stockage des images. Aujourd’hui mes images sont stockées directement dans l’arbre en base64, ce qui ralentit énormément l’affichage de l’écran de restitution. J’essayerais de stocker ça avec le storage de firebase plutôt qu’en tant que nœud firebase…</li></ul><p>Si vous êtes curieux, je vous invite à consulter le code source : <a href="https://github.com/GDG-Nantes/CountDownDevFest2016">Legonnary-Github</a></p><p>Le résultat final : </p><ul><li><a href="https://legonnary.firebaseapp.com/">App de base</a></li><li><a href="https://legonnary.firebaseapp.com/moderator.html">App Modérateur</a> : Nécessite d’être admin</li><li><a href="https://legonnary.firebaseapp.com/screen.html">App Comptes à rebours</a> : Nécessite d’être admin</li><li><a href="https://legonnary.firebaseapp.com/summary.html">App Restit</a></li></ul><script type="text/javascript" src="/assets/js_helper/jef-binomed-helper.js"></script><script type="text/javascript" src="/assets/2016-12-legonnary/legonnary.js"></script>]]></content>
<summary type="html"><p><img src="/assets/2016-12-legonnary/devfest_photo.jpg"></p>
<p>Troisième partie d’une série de 3 articles : </p>
<ul>
<li><a href="/2016/</summary>
<category term="Tech" scheme="http://jef.binomed.fr/categories/Tech/"/>
<category term="HTML5" scheme="http://jef.binomed.fr/tags/HTML5/"/>
<category term="DevFest" scheme="http://jef.binomed.fr/tags/DevFest/"/>
<category term="CSS" scheme="http://jef.binomed.fr/tags/CSS/"/>
</entry>
</feed>