diff --git a/gui/include/overlay.hpp b/gui/include/overlay.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..2339c43c18a12f0bcc7c2c79b9a4a992769b89b1
--- /dev/null
+++ b/gui/include/overlay.hpp
@@ -0,0 +1,30 @@
+#pragma once
+#include <string>
+#include <glad/glad.h>
+#include <glm/glm.hpp>
+#include <shader.hpp>
+
+class Overlay {
+public:
+	static void init();
+	static void loadImage(std::string filename);
+
+	static void draw(glm::mat4 projection, glm::mat4 view, glm::mat4 model);
+
+	//controlled by gui
+	static float radians;
+	static glm::vec2 offset;
+	static float scale;
+	static float opacity;
+
+private:
+	static int _width, _height, _nrChannels;
+	static unsigned char* _data;
+	static GLuint _texture;
+	static Shader _shader;
+	static GLuint _vbo;
+	static GLuint _vao;
+	static GLuint _ebo;
+
+	static bool _imageLoaded;
+};
\ No newline at end of file
diff --git a/gui/src/overlay.cpp b/gui/src/overlay.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ebc8d4ba9b3fdea8de47a2e1d01f6d4443163dc2
--- /dev/null
+++ b/gui/src/overlay.cpp
@@ -0,0 +1,131 @@
+#include <overlay.hpp>
+#include <vector>
+#include <paths.hpp>
+#include <shader.hpp>
+#define STB_IMAGE_IMPLEMENTATION
+#include <stb_image.h>
+#include <iostream>
+#include <INIReader.h>
+
+float Overlay::radians = 0.f;
+glm::vec2 Overlay::offset = { 0.f, 0.f };
+float Overlay::scale = 0.f;
+float Overlay::opacity = 0.f;
+
+int Overlay::_width = -1;
+int Overlay::_height = -1;
+int Overlay::_nrChannels = 0;
+unsigned char* Overlay::_data = nullptr;
+GLuint Overlay::_texture = 0;
+Shader Overlay::_shader = {};
+GLuint Overlay::_vbo = 0;
+GLuint Overlay::_vao = 0;
+GLuint Overlay::_ebo = 0;
+
+bool Overlay::_imageLoaded = false;
+
+void Overlay::init() {
+	//generate opengl objects
+  glGenBuffers(1, &_vbo);
+  glGenBuffers(1, &_ebo);
+  glGenVertexArrays(1, &_vao);
+  glGenTextures(1, &_texture);
+
+  //setup texture parameters
+  glBindTexture(GL_TEXTURE_2D, _texture);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+  glBindTexture(GL_TEXTURE_2D, 0);
+
+  //setup shader
+  INIReader inireader("config.ini");
+  std::string vertFile = inireader.GetString("paths", "pathShaders", "NONE") + std::string("overlayShader.vert");
+  std::string fragFile = inireader.GetString("paths", "pathShaders", "NONE") + std::string("overlayShader.frag");
+  _shader = Shader(vertFile.c_str(), fragFile.c_str());
+  _shader.use();
+  _shader.setFloat("opacity", opacity);
+  _shader.setFloat("radians", radians);
+  _shader.setFloat("scale", scale);
+  _shader.setVec2("offset", offset);
+  _shader.setInt("imTexture", 0);
+  _shader.unuse();
+}
+
+void Overlay::loadImage(std::string filename) {
+  //load image
+  std::cout << "Loading " << filename << std::endl;
+  _data = stbi_load(filename.c_str(), &_width, &_height, &_nrChannels, 0);
+  std::cout << "Loaded, " << _width << " x " << _height << ", " << _nrChannels << " channels." << std::endl;
+  if (!_data) {
+    std::cout << "Error loading overlay file " << filename << std::endl;
+    return;
+  }
+
+  glBindTexture(GL_TEXTURE_2D, _texture);
+  std::cout << "Uploading Texture" << std::endl;
+  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _width, _height, 0, GL_RGB, GL_UNSIGNED_BYTE, _data);
+  glGenerateMipmap(GL_TEXTURE_2D);
+  glBindTexture(GL_TEXTURE_2D, 0);
+  std::cout << "Freeing data" << std::endl;
+  stbi_image_free(_data);
+  std::cout << "Done loading image." << std::endl;
+
+  //generate quad
+  float ratio = float(_height) / float(_width);
+  float length_half = 40.f;
+  std::vector<float> vbo_vec = {
+    //position                              //uv
+    -length_half, -length_half * ratio,     0.f, 0.f, //bottom left
+    length_half, -length_half * ratio,      1.f, 0.f, //bottom right
+    length_half, length_half * ratio,       1.f, 1.f, //top right
+    -length_half, length_half * ratio,      0.f, 1.f  //top left
+  };
+  std::vector<unsigned> ebo_vec = {
+    0, 1, 2, //lower right triangle 
+    0, 2, 3  //top left triangle
+  };
+
+  //Upload EBO data
+  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ebo);
+  glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned) * ebo_vec.size(), ebo_vec.data(), GL_STATIC_DRAW);
+  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+  //Upload VBO data
+  glBindBuffer(GL_ARRAY_BUFFER, _vbo);
+  glBufferData(GL_ARRAY_BUFFER, sizeof(float) * vbo_vec.size(), vbo_vec.data(), GL_STATIC_DRAW);
+
+  //Setup VAO
+  glBindVertexArray(_vao);
+  glBindBuffer(GL_ARRAY_BUFFER, _vbo);
+  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ebo);
+  glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(0));
+  glEnableVertexAttribArray(0);
+  glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));
+  glEnableVertexAttribArray(1);
+  glBindVertexArray(0);
+  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+  glBindBuffer(GL_ARRAY_BUFFER, 0);
+}
+
+void Overlay::draw(glm::mat4 projection, glm::mat4 view, glm::mat4 model) {
+  _shader.use();
+  _shader.setFloat("opacity", opacity);
+  _shader.setFloat("radians", radians);
+  _shader.setFloat("scale", scale);
+  _shader.setVec2("offset", offset);
+  _shader.setInt("imTexture", 0);
+  glBindVertexArray(_vao);
+  glBindBuffer(GL_ARRAY_BUFFER, _vbo);
+  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ebo);
+  glActiveTexture(GL_TEXTURE0);
+  glBindTexture(GL_TEXTURE_2D, _texture);
+  glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
+  glBindVertexArray(0);
+  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+  glBindBuffer(GL_ARRAY_BUFFER, 0);
+  glBindTexture(GL_TEXTURE_2D, 0);
+
+  _shader.unuse();
+}
\ No newline at end of file