diff --git a/.prettierrc b/.prettierrc
index ab28f3ed10d5299b8494cdc778a3f0a4e2af2f26..459ab67beddc6c543f7bb223fd1eb652198954ac 100644
--- a/.prettierrc
+++ b/.prettierrc
@@ -1,3 +1,24 @@
 {
-    "tabWidth": 4
-}
\ No newline at end of file
+    "tabWidth": 4,
+    "overrides": [
+        {
+            "files": [
+                "*.yml",
+                "*.yaml"
+            ],
+            "options": {
+                "tabWidth": 2
+            }
+        },
+        {
+            "files": [
+                "*.js"
+            ],
+            "options": {
+                "tabWidth": 2,
+                "singleQuote": true,
+                "semi": false
+            }
+        }
+    ]
+}
diff --git a/html/css/site.css b/html/css/site.css
index 57cafb873a13cd11334e6894ed1bc34d92259c66..f6e4bcfea29e37dc7795aafce84c7c37b776bb27 100644
--- a/html/css/site.css
+++ b/html/css/site.css
@@ -134,6 +134,14 @@ body {
     text-align: center;
 }
 
+#score {
+    font-size: 22pt;
+    font-weight: bold;
+    margin-top: 320px;
+    text-align: center;
+    display: none;
+}
+
 #gauges-container {
     z-index: 1;
     display: flex;
diff --git a/html/index.html b/html/index.html
index 2c1072ca5d52a8f6a3bc3120d996706f797c5b34..4131b750fb5d104c7d2f47291cf8f190ca1b495f 100644
--- a/html/index.html
+++ b/html/index.html
@@ -44,15 +44,21 @@
             </div>
 
             <div id="animation-container" class="game-inside">
-
+                <video id="animation">
+                    <source src="" type="video/mp4">
+                    Your browser does not support the video tag.
+                </video>
             </div>
-            <div id="score-container" class="game-inside">
 
+            <div id="score-container" class="game-inside">
+                <div id="score">
+                    <span id="score-value">--</span>
+                </div>
             </div>
         </div>
         <div class="video-container">
             <video id="video" autoplay muted loop>
-                <source src="/media/HEIA-FR-Panels-768x384-whith-videos.mp4" type="video/mp4">
+                <source src="/movie/HEIA-FR-Panels-768x384-FR-DE.mp4" type="video/mp4">
                 Your browser does not support the video tag.
             </video>
         </div>
diff --git a/html/js/site.js b/html/js/site.js
index 2ac9d2415b772794df998a143e4bf988e4507829..f363a83a780edfeb1dba2e5b1abb0bc9f5a1ff35 100644
--- a/html/js/site.js
+++ b/html/js/site.js
@@ -1,190 +1,220 @@
-var startTime = Date.now();
-var interval = undefined;
+let startTime = Date.now() // Used for chrono
+let interval // Used for chrono
+const durationActive = {}
 
 function play_video(obj) {
-    console.log("Playing video : ", obj);
-    video_id = obj.id || "video";
-    const v = $(video_id);
-
-    if (v.length == 0) {
-        console.log("Video element not found");
-        return;
-    }
-
-    if (obj.loop) {
-        v.attr("loop", "loop");
-    } else {
-        v.removeAttr("loop");
-    }
-
-    v.find("source").attr("src", obj.src);
-    v[0].currentTime = 0;
-    v[0].load();
+  console.log('Playing video : ', obj)
+  let video_id = '#video'
+  if ('id' in obj) {
+    video_id = '#' + obj['id']
+  }
+  const v = $(video_id).first()
+
+  const doPlay = function () {
+    v.attr('src', obj.src)
+    v.attr('currentTime', 0)
+    $.each(v, function (_, i) {
+      i.load()
+    })
 
     if (obj.volume > 0) {
-        v[0].volume = obj.volume;
-    }
-    v[0].play();
-
-    fade_in = obj["fade-in"] || 0;
-    fade_out = obj["fade-out"] || 0;
-    duration = obj["duration"] || 0;
-
-    if (fade_in > 0 && fade_out > 0 && duration > fade_in + fade_out) {
-        v.fadeIn(fade_in * 1000)
-            .delay((duration - fade_in - fade_out) * 1000)
-            .fadeOut(fade_out * 1000, function () {
-                v[0].pause();
-                v[0].currentTime = 0;
-            });
-    } else if (fade_in > 0 && fade_out > 0) {
-        v.fadeIn(fade_in * 1000).fadeOut(fade_out * 1000, function () {
-            v[0].pause();
-            v[0].currentTime = 0;
-        });
-    } else if (fade_in > 0) {
-        v.fadeIn(fade_in * 1000);
+      v.attr('volume', obj.volume)
     }
+    $.each(v, function (_, i) {
+      i.play()
+    })
+  }
+
+  const doStop = function () {
+    $.each(v, function (_, i) {
+      i.pause()
+    })
+    v.attr('currentTime', 0)
+  }
+
+  if (obj.loop) {
+    v.attr('loop', true)
+  } else {
+    v.removeAttr('loop')
+  }
+
+  let fadeIn = 200
+  if ('fade-in' in obj) fadeIn = obj['fade-in'] * 1000
+  let prevFadeOut = 200
+  if ('prev-fade-out' in obj) prevFadeOut = obj['prev-fade-out'] * 1000
+  let fadeOut = 0
+  if ('fade-out' in obj) fadeOut = obj['fade-out'] * 1000
+  let duration = 0
+  if ('duration' in obj) duration = obj.duration * 1000
+
+  if (durationActive[video_id] !== undefined) {
+    clearTimeout(durationActive[video_id])
+    durationActive[video_id] = undefined
+  }
+  v.stop(true, true)
+  v.clearQueue()
+
+  if (fadeOut > 0 && duration > fadeIn + fadeOut) {
+    v.fadeOut(prevFadeOut, doPlay).fadeIn(fadeIn)
+    durationActive[video_id] = setTimeout(() => {
+      v.fadeOut(fadeOut, function () {
+        doStop()
+        durationActive[video_id] = undefined
+      })
+    }, duration - fadeIn - fadeOut)
+  } else if (fadeOut > 0) {
+    v.fadeOut(prevFadeOut, doPlay).fadeIn(fadeIn).fadeOut(fadeOut, doStop)
+  } else {
+    v.fadeOut(prevFadeOut, doPlay).fadeIn(fadeIn)
+  }
 }
 
 function stop_video(obj) {
-    console.log("Stopping video");
-    video_id = obj.id || "video";
-    const v = $(video_id);
-
-    if (v.length == 0) {
-        console.log("Video element not found");
-        return;
-    }
-
-    fade_out = obj["fade-out"] || 0;
-
-    if (fade_out > 0) {
-        v.fadeOut(fade_out * 1000, function () {
-            v.hide();
-            v[0].pause();
-            v[0].currentTime = 0;
-        });
-    } else {
-        v.hide();
-        v[0].pause();
-        v[0].currentTime = 0;
-    }
+  console.log('Stopping video')
+  let video_id = '#video'
+  if ('id' in obj) {
+    video_id = '#' + obj['id']
+  }
+  const v = $(video_id).first()
+
+  const doStop = function () {
+    $.each(v, function (_, i) {
+      i.pause()
+    })
+    v.attr('currentTime', 0)
+  }
+
+  let fadeOut = 200
+  if ('fade-out' in obj) fadeOut = obj['fade-out'] * 1000
+
+  if (durationActive[video_id] !== undefined) {
+    clearTimeout(durationActive[video_id])
+    durationActive[video_id] = undefined
+  }
+
+  v.fadeOut(fadeOut, doStop)
 }
 
 function start_game(obj) {
-    console.log("Starting game");
+  console.log('Starting game')
 }
 
 // ----- Teams -----
 
 function set_teams(teama, teamb) {
-    $("#team-left").html(teama);
-    $("#team-right").html(teamb);
+  $('#team-left').html(teama)
+  $('#team-right').html(teamb)
 }
 
 // ----- Gauges -----
 
 function set_left_gauge(value) {
-    $("#gauge-left").height(value + "%");
-    $("#level-left").html(value + "%");
+  $('#gauge-left').height(value + '%')
+  $('#level-left').html(value + '%')
 }
 
 function set_right_gauge(value) {
-    $("#gauge-right").height(value + "%");
-    $("#level-right").html(value + "%");
+  $('#gauge-right').height(value + '%')
+  $('#level-right').html(value + '%')
 }
 
 // ----- Chrono -----
 
 function start_chrono() {
-    startTime = Date.now();
-    if (interval !== undefined) {
-        clearInterval(interval);
-    }
-    interval = setInterval(function () {
-        var elapsedTime = Date.now() - startTime;
-        $("#timer-value").html(
-            luxon.DateTime.fromMillis(elapsedTime).toFormat("mm:ss.uu")
-        );
-    });
+  startTime = Date.now()
+  if (interval !== undefined) {
+    clearInterval(interval)
+  }
+  interval = setInterval(function () {
+    const elapsedTime = Date.now() - startTime
+    $('#timer-value').html(
+      luxon.DateTime.fromMillis(elapsedTime).toFormat('mm:ss.uu')
+    )
+    $('#score-value').html(
+      luxon.DateTime.fromMillis(elapsedTime).toFormat('mm:ss.uu')
+    )
+  })
 }
 
 function stop_chrono() {
-    if (interval !== undefined) {
-        clearInterval(interval);
-    }
+  if (interval !== undefined) {
+    clearInterval(interval)
+  }
 }
 
 function reset_chrono() {
-    stop_chrono();
-    $("#timer-value").html(luxon.DateTime.fromMillis(0).toFormat("mm:ss.uu"));
+  stop_chrono()
+  $('#timer-value').html(luxon.DateTime.fromMillis(0).toFormat('mm:ss.uu'))
+  $('#score-value').html(luxon.DateTime.fromMillis(0).toFormat('mm:ss.uu'))
 }
 
 // MQTT client
 
 function init_client(broker_url, base_topic) {
-    console.log("Connecting to broker:", broker_url);
-    const client = mqtt.connect(broker_url);
-
-    client.on("connect", function (connack) {
-        console.log("Connected to broker");
-        console.log("Subscribing to topic:", base_topic + "#");
-        client.subscribe(base_topic + "#", { qos: 1 });
-    });
-
-    const v = $("#video");
-    v[0].addEventListener("ended", (event) => {
-        client.publish(base_topic + "/video/ended", "true");
-    });
-
-    client.on("message", function (topic, message) {
-        // console.log(
-        //     "Received message:",
-        //     topic.toString(),
-        //     " : ",
-        //     message.toString()
-        // );
-
-        var obj;
-        try {
-            obj = JSON.parse(message);
-        } catch (error) {
-            console.log("Error parsing message");
-            obj = {};
-        }
-
-        // console.log("Parsed message:", obj);
-        switch (topic) {
-            case base_topic + "reload":
-                location.reload(true);
-                break;
-            case base_topic + "gauge/left":
-                set_left_gauge(obj.value);
-                break;
-            case base_topic + "gauge/right":
-                set_right_gauge(obj.value);
-                break;
-            case base_topic + "chrono/start":
-                start_chrono();
-                break;
-            case base_topic + "chrono/stop":
-                stop_chrono();
-                break;
-            case base_topic + "chrono/reset":
-                reset_chrono();
-                break;
-            case base_topic + "video/play":
-                play_video(obj);
-                break;
-            case base_topic + "video/stop":
-                stop_video(obj);
-                break;
-            case base_topic + "game/start":
-                start_game(obj);
-                break;
-            default:
-                console.log("Unknown topic:", topic);
-        }
-    });
+  console.log('Connecting to broker:', broker_url)
+  const client = mqtt.connect(broker_url)
+
+  client.on('connect', function (connack) {
+    console.log('Connected to broker')
+    console.log('Subscribing to topic:', base_topic + '#')
+    client.subscribe(base_topic + '#', { qos: 1 })
+  })
+
+  const v = $('#video')
+  v[0].addEventListener('ended', (event) => {
+    client.publish(base_topic + '/video/ended', 'true')
+  })
+
+  client.on('message', function (topic, message) {
+    // console.log(
+    //     "Received message:",
+    //     topic.toString(),
+    //     " : ",
+    //     message.toString()
+    // );
+
+    let obj
+    try {
+      obj = JSON.parse(message)
+    } catch (error) {
+      console.log('Error parsing message')
+      obj = {}
+    }
+
+    // console.log("Parsed message:", obj);
+    switch (topic) {
+      case base_topic + 'reload':
+        location.reload(true)
+        break
+      case base_topic + 'gauge/left':
+        set_left_gauge(obj.value)
+        break
+      case base_topic + 'gauge/right':
+        set_right_gauge(obj.value)
+        break
+      case base_topic + 'chrono/start':
+        start_chrono()
+        break
+      case base_topic + 'chrono/stop':
+        stop_chrono()
+        break
+      case base_topic + 'chrono/reset':
+        reset_chrono()
+        break
+      case base_topic + 'video/play':
+        play_video(obj)
+        break
+      case base_topic + 'video/stop':
+        stop_video(obj)
+        break
+      case base_topic + 'score/show':
+        $('#score').fadeIn('fast')
+        break
+      case base_topic + 'score/hide':
+        $('#score').fadeOut('fast')
+        break
+      default:
+        console.log('Unknown topic:', topic)
+    }
+  })
 }
diff --git a/justfile b/justfile
index 918c161e8bb23ad18011c52ee5a64e7978a5f6f9..95df2e3929ecf23eefe1dea998ebbddb2270be6f 100644
--- a/justfile
+++ b/justfile
@@ -1,5 +1,8 @@
 up:
     docker compose --file docker/compose-dev.yaml up --build --force-recreate
 
+up-nginx:
+    docker compose --file docker/compose-dev.yaml up nginx --build --force-recreate
+
 down:
     docker compose --file docker/compose-dev.yaml down
\ No newline at end of file
diff --git a/test.http b/test.http
index 6f48d8abf60c2e756dc56502a19c3fe9b17453c0..30552d111e7345ff53715dd365241d93654056bd 100644
--- a/test.http
+++ b/test.http
@@ -1,13 +1,66 @@
 MQTT tcp://localhost:1883
-Topic: heia/ms/rpi/top/text/show
+Topic: heiafr/ms/top/gauge/left
 
-{"html":"HELLO", "fade-in": 5}
-```
+{
+    "value": 90
+}
+###
+MQTT tcp://localhost:1883
+Topic: heiafr/ms/top/gauge/right
+
+{
+    "value": 70
+}
+###
+MQTT tcp://localhost:1883
+Topic: heiafr/ms/top/chrono/start
+
+1
+###
+MQTT tcp://localhost:1883
+Topic: heiafr/ms/top/chrono/stop
+
+1
+###
+MQTT tcp://localhost:1883
+Topic: heiafr/ms/top/chrono/reset
+
+1
+###
+MQTT tcp://localhost:1883
+Topic: heiafr/ms/top/video/play
+
+{
+    "id": "animation",
+    "src": "movie/Panel-256x384-COMPTEUR-START.mp4",
+    "duration": 6,
+    "fade-out": 1,
+    "fade-in": 0
+}
+###
+MQTT tcp://localhost:1883
+Topic: heiafr/ms/top/video/play
+
+{
+    "id": "animation",
+    "src": "movie/Panel-256x384-WINNER-RED.mp4",
+    "fade-in": 1
+}
+###
+MQTT tcp://localhost:1883
+Topic: heiafr/ms/top/video/stop
+
+{
+    "id": "animation",
+    "fade-out": 2
+}
+###
+MQTT tcp://localhost:1883
+Topic: heiafr/ms/top/score/show
 
-``` http
+1
 ###
 MQTT tcp://localhost:1883
-Topic: heia/ms/rpi/top/text/hide
+Topic: heiafr/ms/top/score/hide
 
-{"fade-out": 5}
-```
\ No newline at end of file
+1
\ No newline at end of file