diff --git a/config/Sources.cmake b/config/Sources.cmake
index c69abf89b7d701175cd5620de09ec7f212b0c367..9a9f55747194ab9972254d5bf57ac11735607b06 100644
--- a/config/Sources.cmake
+++ b/config/Sources.cmake
@@ -2,6 +2,8 @@
 set(vkcv_sources
 		${vkcv_source}/vkcv/Context.hpp
 		${vkcv_source}/vkcv/Context.cpp
+		${vkcv_source}/vkcv/Window.hpp
+		${vkcv_source}/vkcv/Window.cpp
 		${vkcv_source}/vkcv/CoreManager.hpp
 		${vkcv_source}/vkcv/CoreManager.cpp
 )
diff --git a/projects/first_triangle/src/main.cpp b/projects/first_triangle/src/main.cpp
index 542bec6b20a7bb5d52fb5c7e2511e9f0dc6f3de5..ab70b7b74909cc679e3c2bea1d77e8ab39bfe7e3 100644
--- a/projects/first_triangle/src/main.cpp
+++ b/projects/first_triangle/src/main.cpp
@@ -1,8 +1,18 @@
 
 #include <iostream>
 #include <vkcv/Context.hpp>
+#include <vkcv/Window.hpp>
+#include <vkcv/CoreManager.hpp>
 
 int main(int argc, const char** argv) {
+
+    vkcv::initGLFW();
+	vkcv::Window window = vkcv::Window::create(
+		"first triangle",
+        800,
+        600,
+		false
+	);
 	vkcv::Context context = vkcv::Context::create(
 			"First Triangle",
 			VK_MAKE_VERSION(0, 0, 1)
@@ -23,5 +33,9 @@ int main(int argc, const char** argv) {
 		default: std::cout << "Unknown GPU vendor?! Either you're on an exotic system or your driver is broken..." << std::endl;
 	}
 
+	while (window.isWindowOpen()) {
+		window.pollEvents();
+	}
+    vkcv::terminateGLFW();
 	return 0;
 }
diff --git a/src/vkcv/Window.cpp b/src/vkcv/Window.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..521373d1c585e2748ec83020a85767edd050ef4f
--- /dev/null
+++ b/src/vkcv/Window.cpp
@@ -0,0 +1,50 @@
+#include "Window.hpp"
+#include "CoreManager.hpp"
+
+namespace vkcv {
+
+    Window::Window(GLFWwindow *window)
+            : m_window(window) {
+    }
+
+    Window::~Window() {
+        glfwDestroyWindow(m_window);
+        vkcv::terminateGLFW();
+    }
+
+    Window Window::create(const char *windowTitle, int width, int height, bool resizable) {
+        vkcv::initGLFW();
+        width = width <= 0 ? 1 : width;
+        height = height <= 0 ? 1 : height;
+
+        glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
+        glfwWindowHint(GLFW_RESIZABLE, resizable ? GLFW_TRUE : GLFW_FALSE);
+        GLFWwindow *window;
+        window = glfwCreateWindow(width, height, windowTitle, nullptr, nullptr);
+        return Window(window);
+    }
+
+    bool Window::isWindowOpen() const {
+        return !glfwWindowShouldClose(m_window);
+    }
+
+    void Window::pollEvents() {
+        glfwPollEvents();
+    }
+
+    GLFWwindow *Window::getWindow() const {
+        return m_window;
+    }
+
+    int Window::getWidth() const {
+        int width;
+        glfwGetWindowSize(m_window, &width, nullptr);
+        return width;
+    }
+
+    int Window::getHeight() const {
+        int height;
+        glfwGetWindowSize(m_window, nullptr, &height);
+        return height;
+    }
+}
\ No newline at end of file
diff --git a/src/vkcv/Window.hpp b/src/vkcv/Window.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..2c79287b703065e29e2d32338d9c440fa3123c88
--- /dev/null
+++ b/src/vkcv/Window.hpp
@@ -0,0 +1,41 @@
+#pragma once
+
+#include <vulkan/vulkan.hpp>
+
+#define GLFW_INCLUDE_VULKAN
+
+#include <GLFW/glfw3.h>
+
+namespace vkcv {
+
+    class Window final {
+    private:
+        explicit Window(GLFWwindow *window);
+
+        GLFWwindow *m_window;
+
+    public:
+        static Window create(const char *windowTitle, int width = -1, int height = -1, bool resizable = false);
+
+        [[nodiscard]]
+        bool isWindowOpen() const;
+
+        static void pollEvents();
+
+        [[nodiscard]]
+        GLFWwindow *getWindow() const;
+
+        [[nodiscard]]
+        int getWidth() const;
+
+        [[nodiscard]]
+        int getHeight() const;
+
+        Window &operator=(const Window &other) = delete;
+
+        Window &operator=(Window &&other) = default;
+
+        virtual ~Window();
+
+    };
+}
\ No newline at end of file