diff --git a/scwx-qt/gl/overlay.frag b/scwx-qt/gl/overlay.frag
new file mode 100644
index 00000000..b2fd78c6
--- /dev/null
+++ b/scwx-qt/gl/overlay.frag
@@ -0,0 +1,9 @@
+#version 330 core
+uniform vec4 uColor;
+
+layout (location = 0) out vec4 fragColor;
+
+void main()
+{
+   fragColor = uColor;
+}
diff --git a/scwx-qt/gl/overlay.vert b/scwx-qt/gl/overlay.vert
new file mode 100644
index 00000000..042a1d8a
--- /dev/null
+++ b/scwx-qt/gl/overlay.vert
@@ -0,0 +1,9 @@
+#version 330 core
+layout (location = 0) in vec2 aVertex;
+
+uniform mat4 uMVPMatrix;
+
+void main()
+{
+   gl_Position = uMVPMatrix * vec4(aVertex, 0.0f, 1.0f);
+}
diff --git a/scwx-qt/res/fonts/din1451alt.ttf b/scwx-qt/res/fonts/din1451alt.ttf
new file mode 100644
index 00000000..4f46c82b
Binary files /dev/null and b/scwx-qt/res/fonts/din1451alt.ttf differ
diff --git a/scwx-qt/scwx-qt.cmake b/scwx-qt/scwx-qt.cmake
index c9349a9f..5bb594d3 100644
--- a/scwx-qt/scwx-qt.cmake
+++ b/scwx-qt/scwx-qt.cmake
@@ -60,10 +60,12 @@ set(SRC_GL source/scwx/qt/gl/shader_program.cpp
 set(HDR_MANAGER source/scwx/qt/manager/radar_manager.hpp)
 set(SRC_MANAGER source/scwx/qt/manager/radar_manager.cpp)
 set(HDR_MAP source/scwx/qt/map/map_widget.hpp
+            source/scwx/qt/map/overlay_layer.hpp
             source/scwx/qt/map/radar_layer.hpp
             source/scwx/qt/map/radar_range_layer.hpp
             source/scwx/qt/map/triangle_layer.hpp)
 set(SRC_MAP source/scwx/qt/map/map_widget.cpp
+            source/scwx/qt/map/overlay_layer.cpp
             source/scwx/qt/map/radar_layer.cpp
             source/scwx/qt/map/radar_range_layer.cpp
             source/scwx/qt/map/triangle_layer.cpp)
@@ -76,7 +78,9 @@ set(SRC_VIEW source/scwx/qt/view/radar_view.cpp)
 
 set(RESOURCE_FILES scwx-qt.qrc)
 
-set(SHADER_FILES gl/radar.frag
+set(SHADER_FILES gl/overlay.frag
+                 gl/overlay.vert
+                 gl/radar.frag
                  gl/radar.vert
                  gl/text.frag
                  gl/text.vert)
diff --git a/scwx-qt/scwx-qt.qrc b/scwx-qt/scwx-qt.qrc
index 2638956d..a2e2bec9 100644
--- a/scwx-qt/scwx-qt.qrc
+++ b/scwx-qt/scwx-qt.qrc
@@ -1,8 +1,11 @@
 
     
+        gl/overlay.frag
+        gl/overlay.vert
         gl/radar.frag
         gl/radar.vert
         gl/text.frag
         gl/text.vert
+        res/fonts/din1451alt.ttf
     
 
diff --git a/scwx-qt/source/scwx/qt/map/map_widget.cpp b/scwx-qt/source/scwx/qt/map/map_widget.cpp
index 1fa910e0..d15814ab 100644
--- a/scwx-qt/source/scwx/qt/map/map_widget.cpp
+++ b/scwx-qt/source/scwx/qt/map/map_widget.cpp
@@ -1,5 +1,6 @@
 #include 
 #include 
+#include 
 #include 
 #include 
 
@@ -118,6 +119,8 @@ void MapWidget::AddLayers()
    // QMapboxGL::addCustomLayer will take ownership of the QScopedPointer
    QScopedPointer pHost(
       new RadarLayer(radarView, p->gl_));
+   QScopedPointer pOverlayHost(
+      new OverlayLayer(radarView, p->gl_));
 
    QString before = "ferry";
 
@@ -134,6 +137,7 @@ void MapWidget::AddLayers()
 
    p->map_->addCustomLayer("radar", pHost, before);
    RadarRangeLayer::Add(p->map_, before);
+   p->map_->addCustomLayer("overlay", pOverlayHost);
 }
 
 void MapWidget::keyPressEvent(QKeyEvent* ev)
diff --git a/scwx-qt/source/scwx/qt/map/overlay_layer.cpp b/scwx-qt/source/scwx/qt/map/overlay_layer.cpp
new file mode 100644
index 00000000..a20356a8
--- /dev/null
+++ b/scwx-qt/source/scwx/qt/map/overlay_layer.cpp
@@ -0,0 +1,223 @@
+#include 
+#include 
+#include 
+#include 
+
+#include 
+#include 
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+namespace scwx
+{
+namespace qt
+{
+namespace map
+{
+
+static const std::string logPrefix_ = "[scwx::qt::map::overlay_layer] ";
+
+class OverlayLayerImpl
+{
+public:
+   explicit OverlayLayerImpl(std::shared_ptr radarView,
+                             gl::OpenGLFunctions&             gl) :
+       radarView_(radarView),
+       gl_(gl),
+       textShader_(gl),
+       font_(util::Font::Create(":/res/fonts/din1451alt.ttf")),
+       shaderProgram_(gl),
+       uMVPMatrixLocation_(GL_INVALID_INDEX),
+       uColorLocation_(GL_INVALID_INDEX),
+       vbo_ {GL_INVALID_INDEX},
+       vao_ {GL_INVALID_INDEX},
+       texture_ {GL_INVALID_INDEX},
+       numVertices_ {0},
+       colorTableUpdated_(false),
+       plotUpdated_(true)
+   {
+      // TODO: Manage font at the global level, texture at the view level
+   }
+   ~OverlayLayerImpl() = default;
+
+   std::shared_ptr radarView_;
+   gl::OpenGLFunctions&             gl_;
+
+   gl::TextShader              textShader_;
+   std::shared_ptr font_;
+   gl::ShaderProgram           shaderProgram_;
+   GLint                       uMVPMatrixLocation_;
+   GLint                       uColorLocation_;
+   GLuint                      vbo_;
+   GLuint                      vao_;
+   GLuint                      texture_;
+
+   GLsizeiptr numVertices_;
+
+   bool colorTableUpdated_;
+   bool plotUpdated_;
+};
+
+OverlayLayer::OverlayLayer(std::shared_ptr radarView,
+                           gl::OpenGLFunctions&             gl) :
+    p(std::make_unique(radarView, gl))
+{
+}
+OverlayLayer::~OverlayLayer() = default;
+
+void OverlayLayer::initialize()
+{
+   BOOST_LOG_TRIVIAL(debug) << logPrefix_ << "initialize()";
+
+   gl::OpenGLFunctions& gl = p->gl_;
+
+   p->textShader_.Initialize();
+
+   // Load and configure overlay shader
+   p->shaderProgram_.Load(":/gl/overlay.vert", ":/gl/overlay.frag");
+
+   p->uMVPMatrixLocation_ =
+      gl.glGetUniformLocation(p->shaderProgram_.id(), "uMVPMatrix");
+   if (p->uMVPMatrixLocation_ == -1)
+   {
+      BOOST_LOG_TRIVIAL(warning) << logPrefix_ << "Could not find uMVPMatrix";
+   }
+
+   p->uColorLocation_ =
+      gl.glGetUniformLocation(p->shaderProgram_.id(), "uColor");
+   if (p->uColorLocation_ == -1)
+   {
+      BOOST_LOG_TRIVIAL(warning) << logPrefix_ << "Could not find uColor";
+   }
+
+   if (p->texture_ == GL_INVALID_INDEX)
+   {
+      p->texture_ = p->font_->GenerateTexture(gl);
+   }
+
+   p->shaderProgram_.Use();
+
+   // Generate a vertex array object
+   gl.glGenVertexArrays(1, &p->vao_);
+
+   // Generate vertex buffer objects
+   gl.glGenBuffers(1, &p->vbo_);
+
+   gl.glBindVertexArray(p->vao_);
+
+   // Bottom panel (dynamic sized)
+   gl.glBindBuffer(GL_ARRAY_BUFFER, p->vbo_);
+   gl.glBufferData(
+      GL_ARRAY_BUFFER, sizeof(float) * 6 * 2, nullptr, GL_DYNAMIC_DRAW);
+
+   gl.glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, static_cast(0));
+   gl.glEnableVertexAttribArray(0);
+
+   // Bottom panel color
+   gl.glUniform4f(p->uColorLocation_, 0.0f, 0.0f, 0.0f, 0.75f);
+
+   connect(p->radarView_.get(),
+           &view::RadarView::PlotUpdated,
+           this,
+           &OverlayLayer::ReceivePlotUpdate);
+}
+
+void OverlayLayer::render(const QMapbox::CustomLayerRenderParameters& params)
+{
+   gl::OpenGLFunctions& gl = p->gl_;
+
+   static std::string plotTimeString;
+
+   if (p->plotUpdated_)
+   {
+      using namespace std::chrono;
+      auto plotTime = time_point_cast(p->radarView_->PlotTime());
+
+      if (plotTime.time_since_epoch().count() != 0)
+      {
+         zoned_time         zt = {current_zone(), plotTime};
+         std::ostringstream os;
+         os << zt;
+         plotTimeString = os.str();
+      }
+
+      p->plotUpdated_ = false;
+   }
+
+   glm::mat4 projection = glm::ortho(0.0f,
+                                     static_cast(params.width),
+                                     0.0f,
+                                     static_cast(params.height));
+
+   p->shaderProgram_.Use();
+
+   gl.glUniformMatrix4fv(
+      p->uMVPMatrixLocation_, 1, GL_FALSE, glm::value_ptr(projection));
+
+   // Bottom panel vertices
+   float vertices[6][2] = {{0.0f, 0.0f},
+                           {0.0f, 24.0f},
+                           {static_cast(params.width), 0.0f}, //
+                                                                     //
+                           {0.0f, 24.0f},
+                           {static_cast(params.width), 0.0f},
+                           {static_cast(params.width), 24.0f}};
+
+   // Draw vertices
+   gl.glBindVertexArray(p->vao_);
+   gl.glBindBuffer(GL_ARRAY_BUFFER, p->vbo_);
+   gl.glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
+   gl.glDrawArrays(GL_TRIANGLES, 0, 6);
+
+   // Render time
+   p->textShader_.RenderText(plotTimeString,
+                             params.width - 7.0f,
+                             7.0f,
+                             16.0f,
+                             projection,
+                             boost::gil::rgba8_pixel_t(255, 255, 255, 204),
+                             p->font_,
+                             p->texture_,
+                             gl::TextAlign::Right);
+
+   SCWX_GL_CHECK_ERROR();
+}
+
+void OverlayLayer::deinitialize()
+{
+   gl::OpenGLFunctions& gl = p->gl_;
+
+   BOOST_LOG_TRIVIAL(debug) << logPrefix_ << "deinitialize()";
+
+   gl.glDeleteVertexArrays(1, &p->vao_);
+   gl.glDeleteBuffers(1, &p->vbo_);
+   gl.glDeleteTextures(1, &p->texture_);
+
+   p->uMVPMatrixLocation_ = GL_INVALID_INDEX;
+   p->uColorLocation_     = GL_INVALID_INDEX;
+   p->vao_                = GL_INVALID_INDEX;
+   p->vbo_                = {GL_INVALID_INDEX};
+   p->texture_            = GL_INVALID_INDEX;
+
+   disconnect(p->radarView_.get(),
+              &view::RadarView::PlotUpdated,
+              this,
+              &OverlayLayer::ReceivePlotUpdate);
+}
+
+void OverlayLayer::ReceivePlotUpdate()
+{
+   p->plotUpdated_ = true;
+}
+
+} // namespace map
+} // namespace qt
+} // namespace scwx
diff --git a/scwx-qt/source/scwx/qt/map/overlay_layer.hpp b/scwx-qt/source/scwx/qt/map/overlay_layer.hpp
new file mode 100644
index 00000000..8147fa3f
--- /dev/null
+++ b/scwx-qt/source/scwx/qt/map/overlay_layer.hpp
@@ -0,0 +1,39 @@
+#pragma once
+
+#include 
+#include 
+
+#include 
+
+namespace scwx
+{
+namespace qt
+{
+namespace map
+{
+
+class OverlayLayerImpl;
+
+class OverlayLayer : public QObject, public QMapbox::CustomLayerHostInterface
+{
+   Q_OBJECT
+
+public:
+   explicit OverlayLayer(std::shared_ptr radarView,
+                         gl::OpenGLFunctions&             gl);
+   ~OverlayLayer();
+
+   void initialize() override final;
+   void render(const QMapbox::CustomLayerRenderParameters&) override final;
+   void deinitialize() override final;
+
+public slots:
+   void ReceivePlotUpdate();
+
+private:
+   std::unique_ptr p;
+};
+
+} // namespace map
+} // namespace qt
+} // namespace scwx