diff --git a/.gitignore b/.gitignore
index b4e77fa32739c69ea56da53e8e768fb63951eab0..e227c36f7b7374bacba2c600686e210979634162 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,3 +18,4 @@ build.clang/*
 
 # Local configurations
 .vim/*
+.vscode/*
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a3e88ea5ce9a820890e1612082c2520272c68aed..d5ff07305cb4f88bf378c626121e1ce6c06dd07d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -20,14 +20,23 @@ set(THREADS_PREFER_PTHREAD_FLAG ON)
 find_package(QT NAMES Qt6 Qt5      COMPONENTS Widgets REQUIRED)
 find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets REQUIRED)
 
+if(WIN32)
+    message("You are building on windows, pay attenion to the dependencies")
+    # Needed setup for Vivy to compile on Windows goes here
+endif()
+
 # Find others dependencies
 find_library(AVCODEC_LIBRARY    avcodec     4.0 REQUIRED)
 find_library(AVUTIL_LIBRARY     avutil      4.0 REQUIRED)
 find_library(SWRESAMPLE_LIBRARY swresample      REQUIRED)
 find_library(AVFORMAT_LIBRARY   avformat        REQUIRED)
+find_library(MPV_LIBRARY        mpv             REQUIRED)
 
 # Add the lua dependency
-add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/vendor/lua-5.4.3" "${CMAKE_BINARY_DIR}/vendor/lua-5.4.3")
+add_subdirectory(
+    "${CMAKE_CURRENT_SOURCE_DIR}/vendor/lua-5.4.3"
+    "${CMAKE_BINARY_DIR}/vendor/lua-5.4.3"
+)
 
 # Grab all files
 file(GLOB_RECURSE Vivy_SRC CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cc")
@@ -54,6 +63,7 @@ target_link_libraries(Vivy PRIVATE ${AVCODEC_LIBRARY})
 target_link_libraries(Vivy PRIVATE ${AVUTIL_LIBRARY})
 target_link_libraries(Vivy PRIVATE ${SWRESAMPLE_LIBRARY})
 target_link_libraries(Vivy PRIVATE ${AVFORMAT_LIBRARY})
+target_link_libraries(Vivy PRIVATE ${MPV_LIBRARY})
 target_link_libraries(Vivy PRIVATE lua)
 
 # Headers related things
@@ -107,6 +117,9 @@ target_compile_options(Vivy PRIVATE
 
 target_link_libraries(Vivy PRIVATE -fopenmp)
 
+# Prepare for Qt6
+target_compile_definitions(Vivy PRIVATE QT_DISABLE_DEPRECATED_BEFORE=0x050F00)
+
 # Some compiler specific warnings and options
 if (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")
     target_compile_options(Vivy PRIVATE
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 4d4bae82a96bc372b6c64cd020d64a801e842c8c..a28934fb8dbc059e39cd1acd2f66e68515d05f29 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -42,9 +42,11 @@ the Vivy's lib and the types used in Vivy's UI.
 
 Some of the new features will be used:
 
-- concepts (of course!)
-- coroutines
+- concepts (of course! at least we try)
+- coroutines (not supported by compilers for now)
 - likely and unlikely attributes
+- auto return types in functions and arguments, to pass objects like
+  lambda or other callable
 
 #### Unused features
 
@@ -52,6 +54,6 @@ Some of the feature may not be
 used or not already be in use for multiple reasons:
 
 - the new C++20 modules feature is not used because I don't know how it
-will play with `moc`
+  will play with `moc`. It's not supported by clangd for now anyway
 
 
diff --git a/README.md b/README.md
index 7afecb61aa645eeaa959dac99285630474412f57..ee2312464888bfaa77586c4d5e7b5c57191d8fc6 100644
--- a/README.md
+++ b/README.md
@@ -9,12 +9,10 @@ Simply use cmake to build in another folder of the source folder:
 cmake -Bbuild -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang
 ```
 
-If you want to use the `compile_commands.json`, use the
-`-DCMAKE_EXPORT_COMPILE_COMMANDS=ON` option with cmake and copy the json
-file into the root folder of the project.
-
 To do a debug build, use the debug switch with cmake:
-`-DCMAKE_BUILD_TYPE=Debug`.
+`-DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=ON`. Note that
+the last option is here to generate the `compile_commands.json`, you
+should copy it at the root of the project.
 
 ### Dependencies
 
@@ -24,9 +22,10 @@ way to add the av libraries to it.
 
 #### Ubuntu/debian
 
-Vivy depends on Qt5, libav and more. On ubuntu install the following
-packages: `qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools
-libavutil-dev libavcodec-dev libavformat-dev`.
+Vivy depends on Qt5 and libav. On ubuntu install the following packages:
+`qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libavutil-dev
+libavcodec-dev libavformat-dev libmpv-dev cmake clang clang-format
+libomp-dev`.
 
 #### Arch/Manjaro
 
diff --git a/rsc/VivyRessources.qrc b/rsc/VivyRessources.qrc
index b27b8c63186b315b4fd7cb2fb9e8c355c97c22c7..542123ecd6cfc6901242cd5182f3821252f809ce 100644
--- a/rsc/VivyRessources.qrc
+++ b/rsc/VivyRessources.qrc
@@ -35,6 +35,9 @@
     <file alias="folder.svg">icons/breeze-dark/folder.svg</file>
     <file alias="text-x-generic.svg">icons/breeze-dark/text-x-generic.svg</file>
     <file alias="help-about.svg">icons/breeze-dark/help-about.svg</file>
+    <file alias="media-pause.svg">icons/breeze-dark/media-playback-pause.svg</file>
+    <file alias="media-play.svg">icons/breeze-dark/media-playback-start.svg</file>
+    <file alias="media-stop.svg">icons/breeze-dark/media-playback-stop.svg</file>
 </qresource>
 <qresource prefix="icons/light">
     <file alias="document-new.svg">icons/breeze-light/document-new.svg</file>
@@ -44,6 +47,9 @@
     <file alias="folder.svg">icons/breeze-light/folder.svg</file>
     <file alias="text-x-generic.svg">icons/breeze-light/text-x-generic.svg</file>
     <file alias="help-about.svg">icons/breeze-light/help-about.svg</file>
+    <file alias="media-pause.svg">icons/breeze-light/media-playback-pause.svg</file>
+    <file alias="media-play.svg">icons/breeze-light/media-playback-start.svg</file>
+    <file alias="media-stop.svg">icons/breeze-light/media-playback-stop.svg</file>
 </qresource>
 
 <!-- QDarkStyle style sheet, MIT Licence -->
diff --git a/rsc/icons/breeze-dark/media-playback-pause.svg b/rsc/icons/breeze-dark/media-playback-pause.svg
new file mode 100644
index 0000000000000000000000000000000000000000..6d62eb8de4e89f33c536636878600078a001e6e6
--- /dev/null
+++ b/rsc/icons/breeze-dark/media-playback-pause.svg
@@ -0,0 +1,18 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
+  <defs
+     id="defs3051">
+    <style
+       type="text/css"
+       id="current-color-scheme">
+      .ColorScheme-Text {
+        color:#eff0f1;
+      }
+      </style>
+  </defs>
+  <path
+     style="fill:currentColor;fill-opacity:1;stroke:none"
+     d="m 6 6 0 20 8 0 0 -20 z m 12 0 0 20 8 0 0 -20 z"
+     id="path8"
+     class="ColorScheme-Text"
+     />
+</svg>
diff --git a/rsc/icons/breeze-dark/media-playback-start.svg b/rsc/icons/breeze-dark/media-playback-start.svg
new file mode 100644
index 0000000000000000000000000000000000000000..2014d770a7d1ff5e449d68cefa64421b9e0e91f7
--- /dev/null
+++ b/rsc/icons/breeze-dark/media-playback-start.svg
@@ -0,0 +1,18 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
+  <defs
+     id="defs3051">
+    <style
+       type="text/css"
+       id="current-color-scheme">
+      .ColorScheme-Text {
+        color:#eff0f1;
+      }
+      </style>
+  </defs>
+  <path
+     style="fill:currentColor;fill-opacity:1;stroke:none"
+     d="m 6 6 0 20 20 -10 z"
+     id="path105"
+     class="ColorScheme-Text"
+      />
+</svg>
diff --git a/rsc/icons/breeze-dark/media-playback-stop.svg b/rsc/icons/breeze-dark/media-playback-stop.svg
new file mode 100644
index 0000000000000000000000000000000000000000..3660cf8baaad932135642fe472a347bc45e043ed
--- /dev/null
+++ b/rsc/icons/breeze-dark/media-playback-stop.svg
@@ -0,0 +1,18 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
+  <defs
+     id="defs3051">
+    <style
+       type="text/css"
+       id="current-color-scheme">
+      .ColorScheme-Text {
+        color:#eff0f1;
+      }
+      </style>
+  </defs>
+  <path
+     style="fill:currentColor;fill-opacity:1;stroke:none"
+     d="m 6 6 0 20 20 0 0 -20 z"
+     id="path91"
+     class="ColorScheme-Text"
+     />
+</svg>
diff --git a/rsc/icons/breeze-light/media-playback-pause.svg b/rsc/icons/breeze-light/media-playback-pause.svg
new file mode 100644
index 0000000000000000000000000000000000000000..ece183885e0811d28bcd87beee0639f4fb63f155
--- /dev/null
+++ b/rsc/icons/breeze-light/media-playback-pause.svg
@@ -0,0 +1,18 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
+  <defs
+     id="defs3051">
+    <style
+       type="text/css"
+       id="current-color-scheme">
+      .ColorScheme-Text {
+        color:#232629;
+      }
+      </style>
+  </defs>
+  <path
+     style="fill:currentColor;fill-opacity:1;stroke:none"
+     d="m 6 6 0 20 8 0 0 -20 z m 12 0 0 20 8 0 0 -20 z"
+     id="path8"
+     class="ColorScheme-Text"
+     />
+</svg>
diff --git a/rsc/icons/breeze-light/media-playback-start.svg b/rsc/icons/breeze-light/media-playback-start.svg
new file mode 100644
index 0000000000000000000000000000000000000000..28f7c9b3d05dd7ba88dfaf8ae5bfe456500835a5
--- /dev/null
+++ b/rsc/icons/breeze-light/media-playback-start.svg
@@ -0,0 +1,18 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
+  <defs
+     id="defs3051">
+    <style
+       type="text/css"
+       id="current-color-scheme">
+      .ColorScheme-Text {
+        color:#232629;
+      }
+      </style>
+  </defs>
+  <path
+     style="fill:currentColor;fill-opacity:1;stroke:none"
+     d="m 6 6 0 20 20 -10 z"
+     id="path105"
+     class="ColorScheme-Text"
+      />
+</svg>
diff --git a/rsc/icons/breeze-light/media-playback-stop.svg b/rsc/icons/breeze-light/media-playback-stop.svg
new file mode 100644
index 0000000000000000000000000000000000000000..2aa152a2d0211d2e9492639b8ac469f657481075
--- /dev/null
+++ b/rsc/icons/breeze-light/media-playback-stop.svg
@@ -0,0 +1,18 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
+  <defs
+     id="defs3051">
+    <style
+       type="text/css"
+       id="current-color-scheme">
+      .ColorScheme-Text {
+        color:#232629;
+      }
+      </style>
+  </defs>
+  <path
+     style="fill:currentColor;fill-opacity:1;stroke:none"
+     d="m 6 6 0 20 20 0 0 -20 z"
+     id="path91"
+     class="ColorScheme-Text"
+     />
+</svg>
diff --git a/src/Lib/Utils.hh b/src/Lib/Utils.hh
index 7e75014ad3010e694ee5170830f9ba958a8014b9..ac421be7dd692377f98eb204e1485634734c7efe 100644
--- a/src/Lib/Utils.hh
+++ b/src/Lib/Utils.hh
@@ -1,6 +1,10 @@
 #ifndef VIVY_UTILS_H
 #define VIVY_UTILS_H
 
+#ifndef __cplusplus
+#error "This is a C++ header"
+#endif
+
 #include <QString>
 #include <QFileInfo>
 #include <QStringList>
@@ -9,6 +13,10 @@
 #include <QJsonDocument>
 #include <QtGlobal>
 #include <type_traits>
+#include <chrono>
+
+// Use chrono instead of std::chrono...
+namespace chrono = std::chrono;
 
 // Prety define for OpenMP's parallel for loop with indentation not fucked up
 // by clang-format.
diff --git a/src/UI/DocumentViews/AudioVisualizer.cc b/src/UI/DocumentViews/AudioVisualizer.cc
index 60a6279bde639662a6351f6211bd39504db4d3ac..b7813c425d8f0c39d88a0d10f96b5cece7dd2c62 100644
--- a/src/UI/DocumentViews/AudioVisualizer.cc
+++ b/src/UI/DocumentViews/AudioVisualizer.cc
@@ -37,9 +37,7 @@ AudioVisualizer::AudioVisualizer(AudioContext::StreamPtr stream, QWidget *parent
     RDFTContextPtr ctx(av_rdft_init((static_cast<int>(log2(static_cast<int>(height)))), DFT_R2C),
                        rdftContextDeleter);
 
-    if (!pixels) {
-        throw std::runtime_error("out of memory");
-    } else if (!(chunkData && ctx)) {
+    if (!(chunkData && ctx)) {
         delete[] pixels;
         throw std::runtime_error("out of memory");
     }
diff --git a/src/UI/DocumentViews/MpvContainer.cc b/src/UI/DocumentViews/MpvContainer.cc
new file mode 100644
index 0000000000000000000000000000000000000000..375b9f949828eced98bcf7ae71cf75f5215951c8
--- /dev/null
+++ b/src/UI/DocumentViews/MpvContainer.cc
@@ -0,0 +1,326 @@
+#include "MpvContainer.hh"
+
+#include <mpv/client.h>
+
+using namespace Vivy;
+using namespace std::string_literals;
+
+void
+MpvContainer::mpvEventWakeUpCB(void *user) noexcept
+{
+    MpvContainer *container = reinterpret_cast<MpvContainer *>(user);
+    emit container->mpvEvent();
+}
+
+MpvContainer::MpvContainer(QWidget *parent)
+    : QWidget(parent)
+    , mpv(mpv_create())
+{
+    if (mpv == nullptr)
+        throw std::runtime_error("Failed to create the MPV context");
+
+    setAttribute(Qt::WA_DontCreateNativeAncestors);
+    setAttribute(Qt::WA_NativeWindow);
+
+    quint64 wid = winId();
+    mpv_set_option(mpv, "wid", MPV_FORMAT_INT64, &wid);
+    mpv_set_option_string(mpv, "idle", "yes");
+    mpv_set_option_string(mpv, "loop-file", "inf");
+    mpv_set_option_string(mpv, "no-config", "yes");
+    mpv_set_option_string(mpv, "sid", "no");
+    mpv_set_option_string(mpv, "input-default-bindings", "no");
+    mpv_set_option_string(mpv, "input-vo-keyboard", "no");
+    mpv_request_log_messages(mpv, "info");
+    mpv_observe_property(mpv, 0, "pause", MPV_FORMAT_FLAG);
+    mpv_observe_property(mpv, 0, "duration", MPV_FORMAT_DOUBLE);
+    mpv_observe_property(mpv, 0, "time-pos", MPV_FORMAT_DOUBLE);
+
+    connect(this, &MpvContainer::mpvEvent, this, &MpvContainer::onMpvEvent, Qt::QueuedConnection);
+    mpv_set_wakeup_callback(mpv, &MpvContainer::mpvEventWakeUpCB, this);
+
+    if (int rc = mpv_initialize(mpv); rc < 0) {
+        printMpvError(rc);
+        throw std::runtime_error("Failed to initialize the mpv context");
+    }
+}
+
+void
+MpvContainer::registerMpvTimeCallback(std::function<void(double)> callback) noexcept
+{
+    if (mpvTimeCallback)
+        qWarning() << "Override a previous mpv callback!";
+    mpvTimeCallback = callback;
+}
+
+void
+MpvContainer::registerMpvDurationCallback(std::function<void(double)> callback) noexcept
+{
+    if (mpvDurationCallback)
+        qWarning() << "Override a previous mpv callback!";
+    mpvDurationCallback = callback;
+}
+
+void
+MpvContainer::closeMpv() noexcept
+{
+    if (mpv) {
+        qDebug() << "Closing the MPV context";
+        asyncCommand(AsyncCmdType::None, { "quit", nullptr });
+        registerMpvTimeCallback(nullptr);
+        registerMpvDurationCallback(nullptr);
+        mpv_handle *tmp_mpv = mpv;
+        mpv                 = nullptr; // Stop all other callbacks here
+        mpv_terminate_destroy(tmp_mpv);
+    }
+}
+
+MpvContainer::~MpvContainer() noexcept
+{
+    closeMpv();
+}
+
+void
+MpvContainer::handleMpvEvent(mpv_event *event) noexcept
+{
+    // Declare here variables that can be used in the switch-case statements
+    qint64 w, h;
+    double time;
+    QString msgText;
+    union {
+        mpv_event_log_message *msg;
+        mpv_event_property *prop;
+    };
+
+    auto checkProp = [](mpv_event_property *prop, const std::string &str,
+                        int format) noexcept -> bool {
+        return (prop->name == str) && (prop->format == format);
+    };
+
+    switch (event->event_id) {
+    case MPV_EVENT_SHUTDOWN:
+        closeMpv();
+        break;
+
+    case MPV_EVENT_VIDEO_RECONFIG:
+        // TODO: Those are sync calls, prefer async calls
+        if (mpv_get_property(mpv, "dwidth", MPV_FORMAT_INT64, &w) >= 0 &&
+            mpv_get_property(mpv, "dheight", MPV_FORMAT_INT64, &h) >= 0 && (w > 0 && h > 0)) {
+            qDebug() << "Reconfigure video to" << w << "x" << h;
+        }
+        break;
+
+    case MPV_EVENT_LOG_MESSAGE:
+        msg     = reinterpret_cast<mpv_event_log_message *>(event->data);
+        msgText = msg->text;
+        msgText.replace('\n', "");
+        qDebug().nospace().noquote()
+            << "MPV - MSG [" << msg->prefix << "] " << msg->level << ": " << msgText;
+        break;
+
+    case MPV_EVENT_PROPERTY_CHANGE:
+        prop = reinterpret_cast<mpv_event_property *>(event->data);
+        if (checkProp(prop, "time-pos"s, MPV_FORMAT_DOUBLE) && mpvTimeCallback) {
+            time = *reinterpret_cast<double *>(prop->data);
+            mpvTimeCallback(time);
+        }
+
+        else if (checkProp(prop, "duration"s, MPV_FORMAT_DOUBLE) && mpvDurationCallback) {
+            time = *reinterpret_cast<double *>(prop->data);
+            mpvDurationCallback(time);
+        }
+
+        else if (checkProp(prop, "pause"s, MPV_FORMAT_FLAG)) {
+            isPlaybackPaused = *reinterpret_cast<bool *>(prop->data);
+            emit mpvPlaybackToggled(!isPlaybackPaused);
+            qDebug() << "MPV -> set to" << (isPlaybackPaused ? "pause" : "play");
+        }
+
+        break;
+
+    case MPV_EVENT_PAUSE:
+        isPlaybackPaused = true;
+        emit mpvPlaybackToggled(!isPlaybackPaused);
+        qDebug() << "MPV -> set to pause";
+        break;
+
+    case MPV_EVENT_UNPAUSE:
+        isPlaybackPaused = false;
+        emit mpvPlaybackToggled(!isPlaybackPaused);
+        qDebug() << "MPV -> set to play";
+        break;
+
+    case MPV_EVENT_START_FILE:
+        qDebug() << "MPV: Begin of file";
+        break;
+
+    case MPV_EVENT_END_FILE:
+        qDebug() << "MPV: Reached end of file!";
+        break;
+
+    case MPV_EVENT_COMMAND_REPLY:
+        qDebug() << "Got return of" << event->reply_userdata;
+        handleMpvEventCommandReply(static_cast<AsyncCmdType>(event->reply_userdata));
+        break;
+
+    // Explicitly ignored
+    case MPV_EVENT_NONE:
+    case MPV_EVENT_GET_PROPERTY_REPLY:
+    case MPV_EVENT_SET_PROPERTY_REPLY:
+    case MPV_EVENT_FILE_LOADED:
+    case MPV_EVENT_TRACKS_CHANGED:
+    case MPV_EVENT_TRACK_SWITCHED:
+    case MPV_EVENT_IDLE:
+    case MPV_EVENT_TICK:
+    case MPV_EVENT_SCRIPT_INPUT_DISPATCH:
+    case MPV_EVENT_CLIENT_MESSAGE:
+    case MPV_EVENT_AUDIO_RECONFIG:
+    case MPV_EVENT_METADATA_UPDATE:
+    case MPV_EVENT_SEEK:
+    case MPV_EVENT_PLAYBACK_RESTART:
+    case MPV_EVENT_CHAPTER_CHANGE:
+    case MPV_EVENT_QUEUE_OVERFLOW:
+    case MPV_EVENT_HOOK:
+        break;
+    }
+}
+
+int
+MpvContainer::getAssSid() const noexcept
+{
+    bool conversionOk  = false;
+    const char *result = mpv_get_property_string(mpv, "sid");
+    const int ret      = QString(result).toInt(&conversionOk);
+    return (result == nullptr || !conversionOk) ? -1 : ret;
+}
+
+void
+MpvContainer::handleMpvEventCommandReply(const AsyncCmdType type) noexcept
+{
+    switch (type) {
+    case AsyncCmdType::None:
+        break;
+
+    case AsyncCmdType::LoadAssFile:
+    case AsyncCmdType::ReloadAss:
+        sid = getAssSid();
+        qDebug() << "Load ASS file with id:" << sid;
+        break;
+
+    case AsyncCmdType::UnloadAss:
+        sid = getAssSid();
+        qDebug().nospace() << "Unload Ass, rc = " << sid;
+        if (sid != -1)
+            unloadAssFile();
+        break;
+
+    case AsyncCmdType::LoadFile:
+        sid = getAssSid();
+        qDebug() << "MPV - CMD: File loaded by mpv, sid =" << sid;
+        isPlaybackPaused = false;
+        mpvPause();
+        unloadAssFile();
+        break;
+
+    case AsyncCmdType::SeekTime:
+        qDebug() << "MPV - CMD: Seeked playback";
+        break;
+
+    case AsyncCmdType::TogglePlayback:
+        qDebug() << "MPV - CMD: Playback was toggled";
+        break;
+    }
+}
+
+void
+MpvContainer::onMpvEvent() noexcept
+{
+    while (mpv) {
+        mpv_event *event = mpv_wait_event(mpv, 0);
+        if (event == nullptr || event->event_id == MPV_EVENT_NONE)
+            break;
+        handleMpvEvent(event);
+    }
+
+    if (mpv == nullptr) {
+        qDebug() << "MPV was closed while in the event loop!";
+    }
+}
+
+void
+MpvContainer::loadFile(const QString &filename) noexcept
+{
+    if (filename.isEmpty())
+        return;
+
+    const QByteArray cFileName = filename.toUtf8();
+    asyncCommand(AsyncCmdType::LoadFile, { "loadfile", cFileName.data(), nullptr });
+}
+
+void
+MpvContainer::loadAssFile(const QString &ass) noexcept
+{
+    if (ass.isEmpty())
+        return;
+
+    const QByteArray cFileName = ass.toUtf8();
+    asyncCommand(AsyncCmdType::LoadAssFile, { "sub-add", cFileName.data(), nullptr });
+}
+
+void
+MpvContainer::reloadAssFile() noexcept
+{
+    asyncCommand(AsyncCmdType::ReloadAss, { "sub-reload", nullptr });
+}
+
+void
+MpvContainer::printMpvError(int rc) const noexcept
+{
+    if (rc == MPV_ERROR_SUCCESS)
+        return;
+
+    qCritical() << "MPV error:" << mpv_error_string(rc);
+}
+
+void
+MpvContainer::mpvPlay() noexcept
+{
+    if (isPlaybackPaused)
+        mpvTogglePlayback();
+}
+
+void
+MpvContainer::mpvPause() noexcept
+{
+    if (!isPlaybackPaused)
+        mpvTogglePlayback();
+}
+
+void
+MpvContainer::mpvTogglePlayback() noexcept
+{
+    qDebug() << "MPV: Toggling the playback";
+    asyncCommand(AsyncCmdType::TogglePlayback, { "cycle", "pause", "up", nullptr });
+}
+
+void
+MpvContainer::asyncCommand(const AsyncCmdType cmd,
+                           std::initializer_list<const char *> args) noexcept
+{
+    // NOTE: const_cast here, we have faith in MPV to not change the value of
+    // the temporary pointers here. Should be OK anyway because the `args` init
+    // list is a temporary and should be discarded afer the method call.
+    printMpvError(mpv_command_async(mpv, cmd, const_cast<const char **>(std::data(args))));
+}
+
+void
+MpvContainer::unloadAssFile() noexcept
+{
+    asyncCommand(AsyncCmdType::UnloadAss, { "sub-remove", nullptr });
+}
+
+void
+MpvContainer::seekInFile(const chrono::seconds time) noexcept
+{
+    QByteArray seconds = QString::number(time.count()).toUtf8();
+    asyncCommand(AsyncCmdType::SeekTime, { "seek", seconds.data(), "absolute", nullptr });
+}
diff --git a/src/UI/DocumentViews/MpvContainer.hh b/src/UI/DocumentViews/MpvContainer.hh
new file mode 100644
index 0000000000000000000000000000000000000000..8296cac69656bfd2b8d14b3eb2bac70722744884
--- /dev/null
+++ b/src/UI/DocumentViews/MpvContainer.hh
@@ -0,0 +1,79 @@
+#pragma once
+
+#ifndef __cplusplus
+#error "This is a C++ header"
+#endif
+
+#include "../../Lib/Utils.hh"
+
+#include <functional>
+#include <initializer_list>
+
+extern "C" {
+struct mpv_handle;
+struct mpv_event;
+}
+
+namespace Vivy
+{
+class MpvContainer final : public QWidget {
+    Q_OBJECT
+    VIVY_UNMOVABLE_OBJECT(MpvContainer)
+
+    enum AsyncCmdType : uint64_t {
+        None,
+        LoadFile,
+        LoadAssFile,
+        ReloadAss,
+        UnloadAss,
+        SeekTime,
+        TogglePlayback,
+    };
+
+private:
+    bool isPlaybackPaused{ true };
+    mpv_handle *mpv{ nullptr };
+    qint64 sid{ -1 };
+    std::function<void(double)> mpvTimeCallback{ nullptr };
+    std::function<void(double)> mpvDurationCallback{ nullptr };
+
+public:
+    explicit MpvContainer(QWidget *parent);
+    ~MpvContainer() noexcept override;
+
+    void loadFile(const QString &) noexcept;
+    void loadAssFile(const QString &) noexcept;
+    void reloadAssFile() noexcept;
+
+    void seekInFile(const chrono::seconds time) noexcept;
+
+    // Register a callback for time change, don't use Qt's signals for that.
+    // Here the function will likely be moved if necessary (I hope...).
+    void registerMpvTimeCallback(std::function<void(double)>) noexcept;
+    void registerMpvDurationCallback(std::function<void(double)>) noexcept;
+
+private:
+    void handleMpvEvent(mpv_event *) noexcept;
+    void closeMpv() noexcept;
+    void printMpvError(int) const noexcept;
+    void asyncCommand(const AsyncCmdType, std::initializer_list<const char *>) noexcept;
+    void unloadAssFile() noexcept;
+    void handleMpvEventCommandReply(const AsyncCmdType) noexcept;
+    int getAssSid() const noexcept;
+
+    // Must be static to be passed as a function ptr
+    static void mpvEventWakeUpCB(void *) noexcept;
+
+public slots:
+    void mpvPlay() noexcept;
+    void mpvPause() noexcept;
+    void mpvTogglePlayback() noexcept;
+
+private slots:
+    void onMpvEvent() noexcept;
+
+signals:
+    void mpvEvent();
+    void mpvPlaybackToggled(bool isPlaying);
+};
+}
diff --git a/src/UI/DocumentViews/MpvControls.cc b/src/UI/DocumentViews/MpvControls.cc
new file mode 100644
index 0000000000000000000000000000000000000000..5c490dca4cbb1b12228f4b81e61d7e31b14849cf
--- /dev/null
+++ b/src/UI/DocumentViews/MpvControls.cc
@@ -0,0 +1,72 @@
+#include "MpvControls.hh"
+#include "MpvContainer.hh"
+
+#include <QSlider>
+#include <QHBoxLayout>
+#include <QPushButton>
+
+using namespace Vivy;
+
+MpvControls::MpvControls(MpvContainer *passedContainer, QWidget *parent) noexcept
+    : QWidget(parent)
+    , mpv(passedContainer)
+{
+    auto *progressBar          = new QSlider(this);
+    auto *togglePlaybackButton = new QPushButton(playIcon, "", this); // Be default MPV is paused
+
+    progressBar->setTracking(false);
+    progressBar->setOrientation(Qt::Horizontal);
+
+    mpv->registerMpvDurationCallback([progressBar, this](double time) noexcept -> void {
+        timePosition = chrono::seconds::zero();
+        timeDuration = chrono::seconds(static_cast<long>(time));
+        progressBar->setMaximum(static_cast<int>(timeDuration.count()));
+        progressBar->setValue(0);
+    });
+
+    mpv->registerMpvTimeCallback([progressBar, this](double time) noexcept -> void {
+        if (!progressBar->isSliderDown()) {
+            // The user is not pressing the slider
+            timePosition = chrono::seconds(static_cast<long>(time));
+            progressBar->setValue(static_cast<int>(timePosition.count()));
+        }
+    });
+
+    connect(progressBar, &QAbstractSlider::sliderMoved, this,
+            [this](int value) noexcept -> void { askedSliderPosition = value; });
+
+    connect(progressBar, &QAbstractSlider::actionTriggered, this,
+            [progressBar](int action) noexcept -> void {
+                switch (static_cast<QAbstractSlider::SliderAction>(action)) {
+                case QAbstractSlider::SliderSingleStepAdd:
+                case QAbstractSlider::SliderSingleStepSub:
+                case QAbstractSlider::SliderPageStepAdd:
+                case QAbstractSlider::SliderPageStepSub:
+                    progressBar->setSliderPosition(progressBar->value());
+                    break;
+
+                case QAbstractSlider::SliderAction::SliderMove:
+                case QAbstractSlider::SliderNoAction:
+                case QAbstractSlider::SliderToMinimum:
+                case QAbstractSlider::SliderToMaximum:
+                    break;
+                }
+            });
+
+    connect(progressBar, &QAbstractSlider::sliderReleased, this, [this]() noexcept -> void {
+        qDebug() << "Slider set to" << askedSliderPosition << "max was" << timeDuration.count();
+        timePosition = chrono::seconds(askedSliderPosition);
+        mpv->seekInFile(timePosition);
+    });
+
+    connect(togglePlaybackButton, &QAbstractButton::clicked, mpv, &MpvContainer::mpvTogglePlayback);
+    connect(mpv, &MpvContainer::mpvPlaybackToggled, this,
+            [this, togglePlaybackButton](bool isPlay) noexcept -> void {
+                togglePlaybackButton->setIcon(isPlay ? pauseIcon : playIcon);
+            });
+
+    auto *centralLayout = new QHBoxLayout(this);
+    centralLayout->addWidget(togglePlaybackButton);
+    centralLayout->addWidget(progressBar, 1);
+    setLayout(centralLayout);
+}
diff --git a/src/UI/DocumentViews/MpvControls.hh b/src/UI/DocumentViews/MpvControls.hh
new file mode 100644
index 0000000000000000000000000000000000000000..db00d5900753f8411c2c2f2896aab0d8458708dd
--- /dev/null
+++ b/src/UI/DocumentViews/MpvControls.hh
@@ -0,0 +1,31 @@
+#pragma once
+
+#ifndef __cplusplus
+#error "This is a C++ header"
+#endif
+
+#include "../../Lib/Utils.hh"
+#include "../../VivyApplication.hh"
+#include <QIcon>
+
+namespace Vivy
+{
+class MpvContainer;
+
+class MpvControls final : public QWidget {
+    Q_OBJECT
+    VIVY_UNMOVABLE_OBJECT(MpvControls)
+
+private:
+    MpvContainer *mpv{ nullptr };
+    chrono::seconds timeDuration;
+    chrono::seconds timePosition;
+    int askedSliderPosition{ 0 };
+
+    const QIcon playIcon{ VIVY_ICON_PLAY };
+    const QIcon pauseIcon{ VIVY_ICON_PAUSE };
+
+public:
+    explicit MpvControls(MpvContainer *mpv, QWidget *parent) noexcept;
+};
+}
diff --git a/src/UI/DocumentViews/VideoView.cc b/src/UI/DocumentViews/VideoView.cc
new file mode 100644
index 0000000000000000000000000000000000000000..bc73922e714944733f788251d866f26441bba4e0
--- /dev/null
+++ b/src/UI/DocumentViews/VideoView.cc
@@ -0,0 +1,31 @@
+#include "VideoView.hh"
+#include "MpvContainer.hh"
+#include "MpvControls.hh"
+
+#include <QVBoxLayout>
+
+using namespace Vivy;
+
+VideoView::VideoView(QWidget *parent) noexcept
+    : QWidget(parent)
+    , mpv(new MpvContainer(this))
+{
+    auto *centralLayout = new QVBoxLayout(this);
+
+    centralLayout->addWidget(mpv, 1);
+    centralLayout->addWidget(new MpvControls(mpv, this));
+
+    setLayout(centralLayout);
+}
+
+void
+VideoView::loadFile(const QString &filename) noexcept
+{
+    mpv->loadFile(filename);
+}
+
+void
+VideoView::forceStopPlayback() noexcept
+{
+    mpv->mpvPause();
+}
diff --git a/src/UI/DocumentViews/VideoView.hh b/src/UI/DocumentViews/VideoView.hh
new file mode 100644
index 0000000000000000000000000000000000000000..20a97b8d6c519cd28d0b1d96e670161303582a9c
--- /dev/null
+++ b/src/UI/DocumentViews/VideoView.hh
@@ -0,0 +1,26 @@
+#pragma once
+
+#ifndef __cplusplus
+#error "This is a C++ header"
+#endif
+
+#include "../../Lib/Utils.hh"
+
+namespace Vivy
+{
+class MpvContainer;
+
+class VideoView final : public QWidget {
+    Q_OBJECT
+    VIVY_UNMOVABLE_OBJECT(VideoView)
+
+    MpvContainer *mpv{ nullptr };
+
+public:
+    explicit VideoView(QWidget *parent) noexcept;
+
+public slots:
+    void loadFile(const QString &) noexcept;
+    void forceStopPlayback() noexcept;
+};
+}
diff --git a/src/UI/MainWindow.hh b/src/UI/MainWindow.hh
index 17386985d089c75caaef8345ebd1984cd29f241b..05d2292d0255aabb1c079f3e21cf3f6bcc636482 100644
--- a/src/UI/MainWindow.hh
+++ b/src/UI/MainWindow.hh
@@ -23,7 +23,7 @@ class MainWindow final : public QMainWindow {
     QTabWidget *documents{ nullptr };
     QMenu *viewMenu{ nullptr };
 
-    QMutex aboutWindowMutex{ QMutex::NonRecursive };
+    QMutex aboutWindowMutex;
     AboutWindow *aboutWindow{ nullptr };
 
 public:
diff --git a/src/UI/PropertyModel.cc b/src/UI/PropertyModel.cc
index 5dd71eaaca81359c570771bf09db4c45356602b1..d80604ca44e28994fd8797e6afeba3ed5a1e59f4 100644
--- a/src/UI/PropertyModel.cc
+++ b/src/UI/PropertyModel.cc
@@ -261,22 +261,7 @@ PropertyModel::columnCount(const QModelIndex & /* parent */) const noexcept
 Qt::ItemFlags
 PropertyModel::flags(const QModelIndex &index) const noexcept
 {
-    // Handle the case where the model is not editable
-    if (!editableState) {
-        return /* Qt::ItemIsSelectable | */ QAbstractItemModel::flags(index);
-    }
-
-    // Here the model is editable
-    const int col       = index.column();
-    const Item *item    = static_cast<Item *>(index.internalPointer());
-    const bool isArray  = QJsonValue::Array == item->getType();
-    const bool isObject = QJsonValue::Object == item->getType();
-
-    if ((col == 1) && !(isArray || isObject))
-        return Qt::ItemIsSelectable | Qt::ItemIsEditable | QAbstractItemModel::flags(index);
-
-    else
-        return QAbstractItemModel::flags(index);
+    return QAbstractItemModel::flags(index);
 }
 
 // Get the json stored inside the model
diff --git a/src/UI/PropertyModel.hh b/src/UI/PropertyModel.hh
index c79f0aa831199e4b8726d6adcf37d6c76c734992..d22ab6eccb28c96e34f3e00419b66a519d12f3b8 100644
--- a/src/UI/PropertyModel.hh
+++ b/src/UI/PropertyModel.hh
@@ -71,10 +71,7 @@ public:
         root->setKey(object.getElementName());
     }
 
-    ~PropertyModel() noexcept = default;
-
     void loadJson(const QJsonDocument &json);
-    void setEditable(const bool);
 
     QVariant data(const QModelIndex &, int role) const noexcept override;
     bool setData(const QModelIndex &, const QVariant &v, int r = Qt::EditRole) noexcept override;
@@ -96,7 +93,6 @@ private:
     QJsonValue generateJson(Item *) const noexcept;
     std::unique_ptr<Item> root{ nullptr };
     QStringList headers{};
-    bool editableState{ false };
 };
 
 }
diff --git a/src/UI/Utils.cc b/src/UI/Utils.cc
new file mode 100644
index 0000000000000000000000000000000000000000..95f9269d01b74fab6579a1a68ae28878cea2c62d
--- /dev/null
+++ b/src/UI/Utils.cc
@@ -0,0 +1,12 @@
+#include "Utils.hh"
+#include <QWidget>
+
+using namespace Vivy;
+
+void
+Utils::setTransparentBackgroundForWidget(QWidget *const w) noexcept
+{
+    w->setAttribute(Qt::WA_NoSystemBackground);
+    w->setAttribute(Qt::WA_TranslucentBackground);
+    w->setAttribute(Qt::WA_TransparentForMouseEvents);
+}
diff --git a/src/UI/Utils.hh b/src/UI/Utils.hh
new file mode 100644
index 0000000000000000000000000000000000000000..34b57e2a119bcae629be2901e2332a26d7341836
--- /dev/null
+++ b/src/UI/Utils.hh
@@ -0,0 +1,12 @@
+#pragma once
+
+#ifndef __cplusplus
+#error "This is a C++ header"
+#endif
+
+class QWidget;
+
+namespace Vivy::Utils
+{
+void setTransparentBackgroundForWidget(QWidget *const) noexcept;
+}
diff --git a/src/UI/VivyDocumentView.cc b/src/UI/VivyDocumentView.cc
index 64213d65f39dd15677361015d4c99e6c8d5e7b50..a82725074114229ae36477d7aa9ef5a4a5f84f35 100644
--- a/src/UI/VivyDocumentView.cc
+++ b/src/UI/VivyDocumentView.cc
@@ -1,7 +1,9 @@
 #include "VivyDocumentView.hh"
 #include "PropertyModel.hh"
+#include "Utils.hh"
 #include "DocumentViews/AudioVisualizer.hh"
 #include "DocumentViews/AssLinesView.hh"
+#include "DocumentViews/AssLinesModel.hh"
 #include "../VivyApplication.hh"
 #include "../Lib/Document/VivyDocument.hh"
 
@@ -9,6 +11,8 @@
 #include <QTreeView>
 #include <QVBoxLayout>
 #include <QTableView>
+#include <QWidget>
+#include <QDockWidget>
 
 using namespace Vivy;
 
@@ -75,22 +79,43 @@ VivyDocumentView::getDocumentTabToolTip() const noexcept
 void
 VivyDocumentView::loadVideoView() noexcept
 {
+    if (document->checkDocumentCapabilities(VivyDocument::Capabilities::VideoAble)) {
+        if (!videoView) {
+            videoView = new QDockWidget("Video View", this);
+            videoView->setAllowedAreas(Qt::AllDockWidgetAreas);
+            videoView->setFeatures(QDockWidget::DockWidgetMovable |
+                                   QDockWidget::DockWidgetFloatable |
+                                   QDockWidget::DockWidgetClosable);
+            addDockWidget(Qt::BottomDockWidgetArea, videoView, Qt::Vertical);
+            videoView->setTitleBarWidget(new QWidget(this));
+            Utils::setTransparentBackgroundForWidget(videoView->titleBarWidget());
+        }
+
+        // Kubat: because the dock is "closable", when closed the widget itself
+        // is not deleted, it will be hidden and the content will be deleted.
+        // TODO: Check if it works on more platforms.
+        videoView->setWidget(new VideoView(videoView));
+        qobject_cast<VideoView *>(videoView->widget())
+            ->loadFile(document->getVideoSubDocument()->getFilePath());
+    }
 }
 
 void
 VivyDocumentView::loadAssView() noexcept
 {
-    if (assLines)
-        delDockWidget(&assLines);
-
     if (document->checkDocumentCapabilities(VivyDocument::Capabilities::AssAble)) {
+        if (!assLines) {
+            assLines = new QDockWidget("ASS Lines", this);
+            assLines->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable);
+            assLines->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea |
+                                      Qt::BottomDockWidgetArea);
+            addDockWidget(Qt::BottomDockWidgetArea, assLines, Qt::Vertical);
+            assLines->setTitleBarWidget(new QWidget(this));
+            Utils::setTransparentBackgroundForWidget(assLines->titleBarWidget());
+        }
+
         assModel.reset(new AssLinesModel(document->getAssSubDocument()->getLines()));
-        assLines = new QDockWidget("ASS Lines", this);
         assLines->setWidget(new AssLinesView(assModel.get(), assLines));
-        assLines->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable);
-        assLines->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea |
-                                  Qt::BottomDockWidgetArea);
-        addDockWidget(Qt::BottomDockWidgetArea, assLines, Qt::Vertical);
     }
 }
 
@@ -108,23 +133,24 @@ VivyDocumentView::loadAudioView() noexcept
             return;
         }
 
-        if (visualizer)
-            delDockWidget(&visualizer);
-        visualizer = new QDockWidget("Visualizer", this);
-
-        AudioVisualizer *visualizerInner = new AudioVisualizer(stream, visualizer);
-        if (visualizerInner == nullptr) {
-            qCritical() << "Failed to create visualizer for" << audioDocument->getFilePath();
-            return;
+        if (!visualizer) {
+            visualizer = new QDockWidget("Visualizer", this);
+
+            visualizer->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Minimum);
+            visualizer->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::TopDockWidgetArea |
+                                        Qt::BottomDockWidgetArea);
+            visualizer->setFeatures(QDockWidget::DockWidgetMovable |
+                                    QDockWidget::DockWidgetClosable);
+            addDockWidget(Qt::LeftDockWidgetArea, visualizer, Qt::Horizontal);
+            visualizer->setTitleBarWidget(new QWidget(this));
+            Utils::setTransparentBackgroundForWidget(visualizer->titleBarWidget());
         }
 
-        visualizer->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Minimum);
+        // Kubat: don't check, may throw an error but don't think we can
+        // recover from it.
+        AudioVisualizer *visualizerInner = new AudioVisualizer(stream, visualizer);
         visualizer->setWidget(visualizerInner);
-        visualizer->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::TopDockWidgetArea |
-                                    Qt::BottomDockWidgetArea);
-        visualizer->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable);
         visualizer->layout()->setAlignment(visualizerInner, Qt::AlignTop);
-        addDockWidget(Qt::LeftDockWidgetArea, visualizer, Qt::Horizontal);
     }
 
     else {
@@ -139,7 +165,7 @@ VivyDocumentView::closeDocument() noexcept
              << document.use_count() << ")";
     vivyApp->documentStore.closeDocument(document->getUuid());
 
-    // The visualizer pointer should have been deleted by the
+    // Kubat: the visualizer pointer should have been deleted by the
     // deleteAllContent() call if it was created.
     deleteAllContent();
     visualizer = nullptr;
@@ -155,16 +181,19 @@ VivyDocumentView::getDocumentTabIcon() const noexcept
 void
 VivyDocumentView::openProperties() noexcept
 {
-    if (property)
-        delDockWidget(&property);
-
     propertyModel.reset(new PropertyModel(*document.get()));
-    property        = new QDockWidget("Properties", this);
     QTreeView *view = new QTreeView(property);
     view->setModel(propertyModel.get());
     view->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
     view->expandAll();
+
+    if (!property) {
+        property = new QDockWidget("Properties", this);
+        property->setAllowedAreas(Qt::AllDockWidgetAreas);
+        addDockWidget(Qt::RightDockWidgetArea, property, Qt::Vertical);
+        property->setTitleBarWidget(new QWidget(this));
+        Utils::setTransparentBackgroundForWidget(property->titleBarWidget());
+    }
+
     property->setWidget(view);
-    property->setAllowedAreas(Qt::AllDockWidgetAreas);
-    addDockWidget(Qt::RightDockWidgetArea, property, Qt::Vertical);
 }
diff --git a/src/UI/VivyDocumentView.hh b/src/UI/VivyDocumentView.hh
index 56c54a493826fe4bbe4447c65c5b75a05a34b11a..4cdcc8efc8d9cd6d1a68be9c0627b68f410f82ab 100644
--- a/src/UI/VivyDocumentView.hh
+++ b/src/UI/VivyDocumentView.hh
@@ -5,17 +5,18 @@
 #error "This is a C++ header"
 #endif
 
-#include "../Lib/Document/VivyDocument.hh"
-#include "DocumentViews/AudioVisualizer.hh"
-#include "DocumentViews/AssLinesModel.hh"
 #include "AbstractDocumentView.hh"
-#include "PropertyModel.hh"
 
-#include <QWidget>
-#include <QDockWidget>
+class QDockWidget;
+class QWidget;
 
 namespace Vivy
 {
+class PropertyModel;
+class AssLinesModel;
+class VivyDocument;
+class VideoView;
+
 class VivyDocumentView final : public AbstractDocumentView {
     Q_OBJECT
     VIVY_UNMOVABLE_OBJECT(VivyDocumentView)
@@ -45,6 +46,7 @@ private:
     QDockWidget *visualizer{ nullptr };
     QDockWidget *property{ nullptr };
     QDockWidget *assLines{ nullptr };
+    QDockWidget *videoView{ nullptr };
 };
 
 }
diff --git a/src/VivyApplication.cc b/src/VivyApplication.cc
index 86ef22ab58de0ca87bc7265a8e2b662aa4b19677..70c84396b458884121d7e91e561e57687ba8868f 100644
--- a/src/VivyApplication.cc
+++ b/src/VivyApplication.cc
@@ -4,6 +4,7 @@
 #include <QtGlobal>
 #include <QIcon>
 #include <QFontDatabase>
+#include <locale>
 
 using namespace Vivy;
 
@@ -34,6 +35,9 @@ VivyApplication::setTheme(Theme theme) noexcept
 int
 VivyApplication::exec() noexcept
 {
+    // For MPV
+    std::setlocale(LC_NUMERIC, "C");
+
     // Add fonts
     fontIdMonospace     = QFontDatabase::addApplicationFont(":/fonts/FiraCode-Regular.ttf");
     fontIdMonospaceBold = QFontDatabase::addApplicationFont(":/fonts/FiraCode-Bold.ttf");
diff --git a/src/VivyApplication.hh b/src/VivyApplication.hh
index 4b0b82b988e3b6bbf696b3fe0adbf098d95568fd..a13719cda18f38253f0181147f9559f3ff776f34 100644
--- a/src/VivyApplication.hh
+++ b/src/VivyApplication.hh
@@ -16,6 +16,9 @@
 #define VIVY_ICON_ABOUT   ":icons/dark/help-about.svg"
 #define VIVY_ICON_FILE    ":icons/dark/text-x-generic.svg"
 #define VIVY_ICON_FOLDER  ":icons/dark/folder.svg"
+#define VIVY_ICON_PLAY    ":icons/dark/media-play.svg"
+#define VIVY_ICON_PAUSE   ":icons/dark/media-pause.svg"
+#define VIVY_ICON_STOP    ":icons/dark/media-stop.svg"
 
 #include <QApplication>
 #include <QPixmap>