diff --git a/CMakeModules/CopyYuzuQt5Deps.cmake b/CMakeModules/CopyYuzuQt5Deps.cmake
index 4fef666594d863b5203e9b4c065465addf26e405..1e9810bba219b70283ccfb0f94846d3cf9e5cde4 100644
--- a/CMakeModules/CopyYuzuQt5Deps.cmake
+++ b/CMakeModules/CopyYuzuQt5Deps.cmake
@@ -45,5 +45,8 @@ function(copy_yuzu_Qt5_deps target_dir)
 
     windows_copy_files(yuzu ${Qt5_PLATFORMS_DIR} ${PLATFORMS} qwindows$<$<CONFIG:Debug>:d>.*)
     windows_copy_files(yuzu ${Qt5_STYLES_DIR} ${STYLES} qwindowsvistastyle$<$<CONFIG:Debug>:d>.*)
-    windows_copy_files(yuzu ${Qt5_IMAGEFORMATS_DIR} ${IMAGEFORMATS} qjpeg$<$<CONFIG:Debug>:d>.*)
+    windows_copy_files(yuzu ${Qt5_IMAGEFORMATS_DIR} ${IMAGEFORMATS}
+        qjpeg$<$<CONFIG:Debug>:d>.*
+        qgif$<$<CONFIG:Debug>:d>.*
+        )
 endfunction(copy_yuzu_Qt5_deps)
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 1f852df4bcbc30883c36069fa40f683616e96f14..4cab599b4d48597e719940ad9df7e31b4fd8db60 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -68,6 +68,8 @@ add_executable(yuzu
     game_list_p.h
     game_list_worker.cpp
     game_list_worker.h
+    loading_screen.cpp
+    loading_screen.h
     hotkeys.cpp
     hotkeys.h
     main.cpp
@@ -102,9 +104,10 @@ set(UIS
     configuration/configure_system.ui
     configuration/configure_touchscreen_advanced.ui
     configuration/configure_web.ui
+    compatdb.ui
     hotkeys.ui
+    loading_screen.ui
     main.ui
-    compatdb.ui
 )
 
 file(GLOB COMPAT_LIST
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 40db7a5e920b833ac610b8de15b88ab387c5a7ad..f74cb693a417dc6f1dbd09d84a6f524ed463f37c 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -3,9 +3,7 @@
 #include <QKeyEvent>
 #include <QScreen>
 #include <QWindow>
-
 #include <fmt/format.h>
-
 #include "common/microprofile.h"
 #include "common/scm_rev.h"
 #include "core/core.h"
@@ -17,6 +15,7 @@
 #include "video_core/renderer_base.h"
 #include "video_core/video_core.h"
 #include "yuzu/bootmanager.h"
+#include "yuzu/main.h"
 
 EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {}
 
@@ -114,6 +113,8 @@ GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
 
     InputCommon::Init();
     InputCommon::StartJoystickEventHandler();
+    connect(this, &GRenderWindow::FirstFrameDisplayed, static_cast<GMainWindow*>(parent),
+            &GMainWindow::OnLoadComplete);
 }
 
 GRenderWindow::~GRenderWindow() {
@@ -141,6 +142,10 @@ void GRenderWindow::SwapBuffers() {
     child->makeCurrent();
 
     child->swapBuffers();
+    if (!first_frame) {
+        emit FirstFrameDisplayed();
+        first_frame = true;
+    }
 }
 
 void GRenderWindow::MakeCurrent() {
@@ -309,6 +314,8 @@ void GRenderWindow::InitRenderTarget() {
         delete layout();
     }
 
+    first_frame = false;
+
     // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
     // WA_DontShowOnScreen, WA_DeleteOnClose
     QGLFormat fmt;
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 4e30282155e059d37df45491f504560c01321f59..d1f37e5032c9c207ca6ec97de7c6fa411af1e99c 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -152,6 +152,7 @@ public slots:
 signals:
     /// Emitted when the window is closed
     void Closed();
+    void FirstFrameDisplayed();
 
 private:
     std::pair<unsigned, unsigned> ScaleTouch(const QPointF pos) const;
@@ -171,6 +172,8 @@ private:
     /// Temporary storage of the screenshot taken
     QImage screenshot_image;
 
+    bool first_frame = false;
+
 protected:
     void showEvent(QShowEvent* event) override;
 };
diff --git a/src/yuzu/loading_screen.cpp b/src/yuzu/loading_screen.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0f3c4bb6cba60bb7165fa2aff901cbba868320b3
--- /dev/null
+++ b/src/yuzu/loading_screen.cpp
@@ -0,0 +1,84 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <QBuffer>
+#include <QByteArray>
+#include <QHBoxLayout>
+#include <QIODevice>
+#include <QImage>
+#include <QLabel>
+#include <QPainter>
+#include <QPalette>
+#include <QPixmap>
+#include <QProgressBar>
+#include <QStyleOption>
+#include <QWindow>
+#include "common/logging/log.h"
+#include "core/loader/loader.h"
+#include "ui_loading_screen.h"
+#include "yuzu/loading_screen.h"
+
+// Mingw seems to not have QMovie at all. If QMovie is missing then use a single frame instead of an
+// showing the full animation
+#if !YUZU_QT_MOVIE_MISSING
+#include <QMovie>
+#endif
+
+LoadingScreen::LoadingScreen(QWidget* parent)
+    : QWidget(parent), ui(std::make_unique<Ui::LoadingScreen>()) {
+    ui->setupUi(this);
+    // Progress bar is hidden until we have a use for it.
+    ui->progress_bar->hide();
+}
+
+LoadingScreen::~LoadingScreen() = default;
+
+void LoadingScreen::Prepare(Loader::AppLoader& loader) {
+    std::vector<u8> buffer;
+    if (loader.ReadBanner(buffer) == Loader::ResultStatus::Success) {
+#ifdef YUZU_QT_MOVIE_MISSING
+        QPixmap map;
+        map.loadFromData(buffer.data(), buffer.size());
+        ui->banner->setPixmap(map);
+#else
+        backing_mem =
+            std::make_unique<QByteArray>(reinterpret_cast<char*>(buffer.data()), buffer.size());
+        backing_buf = std::make_unique<QBuffer>(backing_mem.get());
+        backing_buf->open(QIODevice::ReadOnly);
+        animation = std::make_unique<QMovie>(backing_buf.get(), QByteArray("GIF"));
+        animation->start();
+        ui->banner->setMovie(animation.get());
+#endif
+        buffer.clear();
+    }
+    if (loader.ReadLogo(buffer) == Loader::ResultStatus::Success) {
+        QPixmap map;
+        map.loadFromData(buffer.data(), buffer.size());
+        ui->logo->setPixmap(map);
+    }
+}
+
+void LoadingScreen::OnLoadProgress(std::size_t value, std::size_t total) {
+    if (total != previous_total) {
+        ui->progress_bar->setMaximum(total);
+        previous_total = total;
+    }
+    ui->progress_bar->setValue(value);
+}
+
+void LoadingScreen::paintEvent(QPaintEvent* event) {
+    QStyleOption opt;
+    opt.init(this);
+    QPainter p(this);
+    style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
+    QWidget::paintEvent(event);
+}
+
+void LoadingScreen::Clear() {
+#ifndef YUZU_QT_MOVIE_MISSING
+    animation.reset();
+    backing_buf.reset();
+    backing_mem.reset();
+#endif
+}
diff --git a/src/yuzu/loading_screen.h b/src/yuzu/loading_screen.h
new file mode 100644
index 0000000000000000000000000000000000000000..2a6cf1142165e6f26904c9c74032ace6973c54d3
--- /dev/null
+++ b/src/yuzu/loading_screen.h
@@ -0,0 +1,56 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <QWidget>
+
+#if !QT_CONFIG(movie)
+#define YUZU_QT_MOVIE_MISSING 1
+#endif
+
+namespace Loader {
+class AppLoader;
+}
+
+namespace Ui {
+class LoadingScreen;
+}
+
+class QBuffer;
+class QByteArray;
+class QMovie;
+
+class LoadingScreen : public QWidget {
+    Q_OBJECT
+
+public:
+    explicit LoadingScreen(QWidget* parent = nullptr);
+
+    ~LoadingScreen();
+
+    /// Call before showing the loading screen to load the widgets with the logo and banner for the
+    /// currently loaded application.
+    void Prepare(Loader::AppLoader& loader);
+
+    /// After the loading screen is hidden, the owner of this class can call this to clean up any
+    /// used resources such as the logo and banner.
+    void Clear();
+
+    // In order to use a custom widget with a stylesheet, you need to override the paintEvent
+    // See https://wiki.qt.io/How_to_Change_the_Background_Color_of_QWidget
+    void paintEvent(QPaintEvent* event) override;
+
+    void OnLoadProgress(std::size_t value, std::size_t total);
+
+private:
+#ifndef YUZU_QT_MOVIE_MISSING
+    std::unique_ptr<QMovie> animation;
+    std::unique_ptr<QBuffer> backing_buf;
+    std::unique_ptr<QByteArray> backing_mem;
+#endif
+    std::unique_ptr<Ui::LoadingScreen> ui;
+    std::size_t previous_total = 0;
+};
diff --git a/src/yuzu/loading_screen.ui b/src/yuzu/loading_screen.ui
new file mode 100644
index 0000000000000000000000000000000000000000..00579b670917446332ce0fa5d4540aa7d7346aae
--- /dev/null
+++ b/src/yuzu/loading_screen.ui
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>LoadingScreen</class>
+ <widget class="QWidget" name="LoadingScreen">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>746</width>
+    <height>495</height>
+   </rect>
+  </property>
+  <property name="styleSheet">
+   <string notr="true">background-color: rgb(0, 0, 0);</string>
+  </property>
+  <layout class="QVBoxLayout">
+   <property name="spacing">
+    <number>0</number>
+   </property>
+   <property name="leftMargin">
+    <number>0</number>
+   </property>
+   <property name="topMargin">
+    <number>0</number>
+   </property>
+   <property name="rightMargin">
+    <number>0</number>
+   </property>
+   <property name="bottomMargin">
+    <number>0</number>
+   </property>
+   <item>
+    <widget class="QLabel" name="logo">
+     <property name="text">
+      <string/>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+     </property>
+     <property name="margin">
+      <number>30</number>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QProgressBar" name="progress_bar">
+       <property name="styleSheet">
+        <string notr="true">font-size: 26px;</string>
+       </property>
+       <property name="value">
+        <number>0</number>
+       </property>
+       <property name="format">
+        <string>Loading Shaders %v out of %m</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item alignment="Qt::AlignRight|Qt::AlignBottom">
+    <widget class="QLabel" name="banner">
+     <property name="styleSheet">
+      <string notr="true">background-color: black;</string>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+     <property name="margin">
+      <number>30</number>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index f564de994885e6d2b3b9681dc96b3290e61f03ff..68bfa23ab1bd32fe51d68aab4a4e5ab93ba83535 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -92,6 +92,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
 #include "yuzu/game_list.h"
 #include "yuzu/game_list_p.h"
 #include "yuzu/hotkeys.h"
+#include "yuzu/loading_screen.h"
 #include "yuzu/main.h"
 #include "yuzu/ui_settings.h"
 
@@ -411,6 +412,10 @@ void GMainWindow::InitializeWidgets() {
     game_list = new GameList(vfs, this);
     ui.horizontalLayout->addWidget(game_list);
 
+    loading_screen = new LoadingScreen(this);
+    loading_screen->hide();
+    ui.horizontalLayout->addWidget(loading_screen);
+
     // Create status bar
     message_label = new QLabel();
     // Configured separately for left alignment
@@ -897,8 +902,9 @@ void GMainWindow::BootGame(const QString& filename) {
                        .arg(Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc,
                             QString::fromStdString(title_name)));
 
-    render_window->show();
-    render_window->setFocus();
+    loading_screen->Prepare(Core::System::GetInstance().GetAppLoader());
+    loading_screen->show();
+    loading_screen->setFocus();
 
     emulation_running = true;
     if (ui.action_Fullscreen->isChecked()) {
@@ -932,6 +938,8 @@ void GMainWindow::ShutdownGame() {
     ui.action_Load_Amiibo->setEnabled(false);
     ui.action_Capture_Screenshot->setEnabled(false);
     render_window->hide();
+    loading_screen->hide();
+    loading_screen->Clear();
     game_list->show();
     game_list->setFilterFocus();
     setWindowTitle(QString("yuzu %1| %2-%3")
@@ -1505,6 +1513,13 @@ void GMainWindow::OnStopGame() {
     ShutdownGame();
 }
 
+void GMainWindow::OnLoadComplete() {
+    loading_screen->hide();
+    loading_screen->Clear();
+    render_window->show();
+    render_window->setFocus();
+}
+
 void GMainWindow::OnMenuReportCompatibility() {
     if (!Settings::values.yuzu_token.empty() && !Settings::values.yuzu_username.empty()) {
         CompatDB compatdb{this};
@@ -1771,9 +1786,8 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
             this, tr("Confirm Key Rederivation"),
             tr("You are about to force rederive all of your keys. \nIf you do not know what this "
                "means or what you are doing, \nthis is a potentially destructive action. \nPlease "
-               "make "
-               "sure this is what you want \nand optionally make backups.\n\nThis will delete your "
-               "autogenerated key files and re-run the key derivation module."),
+               "make sure this is what you want \nand optionally make backups.\n\nThis will delete "
+               "your autogenerated key files and re-run the key derivation module."),
             QMessageBox::StandardButtons{QMessageBox::Ok, QMessageBox::Cancel});
 
         if (res == QMessageBox::Cancel)
@@ -1818,7 +1832,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
                     errors +
                     tr("<br><br>You can get all of these and dump all of your games easily by "
                        "following <a href='https://yuzu-emu.org/help/quickstart/'>the "
-                       "quickstart guide</a>. Alternatively, you can use another method of dumping "
+                       "quickstart guide</a>. Alternatively, you can use another method of dumping"
                        "to obtain all of your keys."));
         }
 
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 2d705ad54d461d9c47832c1615dd2c20f846824d..e07c892cf26ad8481bc7e075d4288350536f9cd8 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -25,6 +25,7 @@ class GImageInfo;
 class GraphicsBreakPointsWidget;
 class GraphicsSurfaceWidget;
 class GRenderWindow;
+class LoadingScreen;
 class MicroProfileDialog;
 class ProfilerWidget;
 class QLabel;
@@ -109,10 +110,10 @@ signals:
     void WebBrowserFinishedBrowsing();
 
 public slots:
+    void OnLoadComplete();
     void ProfileSelectorSelectProfile();
     void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters);
     void SoftwareKeyboardInvokeCheckDialog(std::u16string error_message);
-
     void WebBrowserOpenPage(std::string_view filename, std::string_view arguments);
 
 private:
@@ -212,6 +213,7 @@ private:
 
     GRenderWindow* render_window;
     GameList* game_list;
+    LoadingScreen* loading_screen;
 
     // Status bar elements
     QLabel* message_label = nullptr;