diff --git a/config/Sources.cmake b/config/Sources.cmake
index b51b37b5eb734021663184c813cdb350018999d9..606dc4c97d62ed284d5967707e07237ebd2f7580 100644
--- a/config/Sources.cmake
+++ b/config/Sources.cmake
@@ -85,6 +85,12 @@ set(vkcv_sources
 		
 		${vkcv_source}/vkcv/SamplerManager.hpp
 		${vkcv_source}/vkcv/SamplerManager.cpp
+
+		${vkcv_source}/vkcv/WindowManager.hpp
+		${vkcv_source}/vkcv/WindowManager.cpp
+
+		${vkcv_source}/vkcv/SwapchainManager.hpp
+		${vkcv_source}/vkcv/SwapchainManager.cpp
         
         ${vkcv_include}/vkcv/DescriptorWrites.hpp
         
diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp
index b82d2edad914a632c5e2ea1be2d08f56b3a912e4..d6e47d26e5c26dab7929ac5905938c2ac0c80125 100644
--- a/include/vkcv/Core.hpp
+++ b/include/vkcv/Core.hpp
@@ -25,6 +25,9 @@
 #include "Event.hpp"
 #include "DrawcallRecording.hpp"
 #include "CommandRecordingFunctionTypes.hpp"
+#include "../../src/vkcv/WindowManager.hpp"
+#include "../../src/vkcv/SwapchainManager.hpp"
+
 
 namespace vkcv
 {
@@ -38,6 +41,8 @@ namespace vkcv
     class SamplerManager;
     class ImageManager;
 	class CommandStreamManager;
+	class WindowManager;
+	class SwapchainManager;
 
 	struct SubmitInfo {
 		QueueType queueType;
@@ -54,18 +59,14 @@ namespace vkcv
          *
          * @param context encapsulates various Vulkan objects
          */
-        Core(Context &&context, Window &window, const Swapchain& swapChain,  std::vector<vk::ImageView> imageViews,
-			const CommandResources& commandResources, const SyncResources& syncResources) noexcept;
+        Core(Context &&context, const CommandResources& commandResources, const SyncResources& syncResources) noexcept;
         // explicit destruction of default constructor
         Core() = delete;
 
-		Result acquireSwapchainImage();
+		Result acquireSwapchainImage(const SwapchainHandle &swapchainHandle);
 
         Context m_Context;
 
-        Swapchain                       m_swapchain;
-        Window&                   		m_window;
-
         std::unique_ptr<PassManager>             m_PassManager;
         std::unique_ptr<GraphicsPipelineManager> m_PipelineManager;
         std::unique_ptr<ComputePipelineManager>  m_ComputePipelineManager;
@@ -74,12 +75,18 @@ namespace vkcv
         std::unique_ptr<SamplerManager>          m_SamplerManager;
         std::unique_ptr<ImageManager>            m_ImageManager;
         std::unique_ptr<CommandStreamManager>    m_CommandStreamManager;
+        std::unique_ptr<WindowManager>           m_WindowManager;
+        std::unique_ptr<SwapchainManager>        m_SwapchainManager;
 
 		CommandResources    m_CommandResources;
 		SyncResources       m_SyncResources;
 		uint32_t            m_currentSwapchainImageIndex;
 
-		event_handle<int,int> e_resizeHandle;
+		/**
+		 * sets up swapchain images
+		 * @param swapchainHandles of swapchain
+		 */
+		void setSwapchainImages(SwapchainHandle handle);
 
     public:
         /**
@@ -119,9 +126,6 @@ namespace vkcv
 
         [[nodiscard]]
         const Context &getContext() const;
-        
-        [[nodiscard]]
-        const Swapchain& getSwapchain() const;
 
         /**
              * Creates a #Core with given @p applicationName and @p applicationVersion for your application.
@@ -138,8 +142,7 @@ namespace vkcv
              * @param[in] deviceExtensions (optional) Requested device extensions
              * @return New instance of #Context
              */
-        static Core create(Window &window,
-                           const char *applicationName,
+        static Core create(const char *applicationName,
                            uint32_t applicationVersion,
                            const std::vector<vk::QueueFlagBits>& queueFlags    = {},
 						   const Features& features = {},
@@ -224,12 +227,73 @@ namespace vkcv
 			bool            supportColorAttachment = false,
 			Multisampling   multisampling = Multisampling::None);
 
+        /**
+         * creates a new window and returns it's handle
+         * @param applicationName window name
+         * @param windowWidth
+         * @param windowHeight
+         * @param resizeable resizeability bool
+         * @return windowHandle
+         */
+		[[nodiscard]]
+		WindowHandle createWindow(
+				const char *applicationName,
+				uint32_t windowWidth,
+				uint32_t windowHeight,
+				bool resizeable);
+
+		/**
+		 * getter for window reference
+		 * @param handle of the window
+		 * @return the window
+		 */
+		[[nodiscard]]
+		Window& getWindow(const WindowHandle& handle );
+
+		/**
+		 * gets the swapchain of the current focused window
+		 * @return swapchain
+		 */
+		[[nodiscard]]
+		Swapchain& getSwapchainOfCurrentWindow();
+
+		/**
+		 * returns the swapchain reference
+		 * @param handle of the swapchain
+		 * @return swapchain
+		 */
+		[[nodiscard]]
+		Swapchain& getSwapchain(const SwapchainHandle& handle);
+
+		/**
+		 * gets the swapchain handle from the window
+		 * @param handle of the window
+		 * @return the swapchain from getSwapchain( SwapchainHandle )
+		 */
+		[[nodiscard]]
+		Swapchain& getSwapchain(const WindowHandle& handle);
+
+		/**
+		 * returns the image width
+		 * @param image handle
+		 * @return imageWidth
+		 */
         [[nodiscard]]
         uint32_t getImageWidth(const ImageHandle& image);
-        
+
+        /**
+         * returns the image height
+         * @param image handle
+         * @return imageHeight
+         */
         [[nodiscard]]
         uint32_t getImageHeight(const ImageHandle& image);
-	
+
+        /**
+         * returns the image format of the image
+         * @param image handle
+         * @return imageFormat
+         */
 		[[nodiscard]]
 		vk::Format getImageFormat(const ImageHandle& image);
 
@@ -256,7 +320,7 @@ namespace vkcv
 		/**
 		 * @brief start recording command buffers and increment frame index
 		*/
-		bool beginFrame(uint32_t& width, uint32_t& height);
+		bool beginFrame(uint32_t& width, uint32_t& height, const WindowHandle &windowHandle);
 
 		void recordDrawcallsToCmdStream(
 			const CommandStreamHandle&      cmdStreamHandle,
@@ -264,7 +328,8 @@ namespace vkcv
 			const GraphicsPipelineHandle    &pipelineHandle,
 			const PushConstants             &pushConstants,
 			const std::vector<DrawcallInfo> &drawcalls,
-			const std::vector<ImageHandle>  &renderTargets);
+			const std::vector<ImageHandle>  &renderTargets,
+			const WindowHandle              &windowHandle);
 
 		void recordMeshShaderDrawcalls(
 			const CommandStreamHandle&              cmdStreamHandle,
@@ -272,7 +337,8 @@ namespace vkcv
 			const GraphicsPipelineHandle            &pipelineHandle,
 			const PushConstants&                    pushConstantData,
             const std::vector<MeshShaderDrawcall>&  drawcalls,
-			const std::vector<ImageHandle>&         renderTargets);
+			const std::vector<ImageHandle>&         renderTargets,
+			const WindowHandle&                     windowHandle);
 
 		void recordComputeDispatchToCmdStream(
 			CommandStreamHandle cmdStream,
@@ -298,7 +364,7 @@ namespace vkcv
 		/**
 		 * @brief end recording and present image
 		*/
-		void endFrame();
+		void endFrame( const WindowHandle& windowHandle );
 
 		/**
 		 * Submit a command buffer to any queue of selected type. The recording can be customized by a
diff --git a/include/vkcv/Handles.hpp b/include/vkcv/Handles.hpp
index 4a1df5852adc0fc8fe924041cb7879ec0e376001..1e6091c97dbe8dfc31013b653f175acede76070b 100644
--- a/include/vkcv/Handles.hpp
+++ b/include/vkcv/Handles.hpp
@@ -115,6 +115,18 @@ namespace vkcv
 		
 	};
 
+	class WindowHandle : public Handle {
+		friend class WindowManager;
+	private:
+		using Handle::Handle;
+	};
+
+	class SwapchainHandle : public Handle {
+		friend class SwapchainManager;
+	private:
+		using Handle::Handle;
+	};
+
     class CommandStreamHandle : public Handle {
         friend class CommandStreamManager;
     private:
diff --git a/include/vkcv/QueueManager.hpp b/include/vkcv/QueueManager.hpp
index 0919d20d8e07fee67ceb2f393c29b4a53c51b857..9d219c9d7a308cac29bec1580bd8894a70f68fb3 100644
--- a/include/vkcv/QueueManager.hpp
+++ b/include/vkcv/QueueManager.hpp
@@ -39,6 +39,14 @@ namespace vkcv {
                 std::vector<std::pair<int, int>> &queuePairsCompute,
                 std::vector<std::pair<int, int>> &queuePairsTransfer);
 
+		/**
+		 * checks for surface support in the queues
+		 * @param physicalDevice to get the Queues
+		 * @param surface that needs to checked
+		 * @return
+		 */
+		static uint32_t checkSurfaceSupport(const vk::PhysicalDevice &physicalDevice, vk::SurfaceKHR &surface);
+
     private:
         std::vector<Queue> m_graphicsQueues;
         std::vector<Queue> m_computeQueues;
diff --git a/include/vkcv/Swapchain.hpp b/include/vkcv/Swapchain.hpp
index 5e9bc7d0593a3b2e1f1f8e7b5ef7ea69e9711fb5..c7741c652926fad5d581ecf7465ed74af9e2db37 100644
--- a/include/vkcv/Swapchain.hpp
+++ b/include/vkcv/Swapchain.hpp
@@ -13,6 +13,8 @@ namespace vkcv
     class Swapchain final {
     private:
     	friend class Core;
+    	friend class Window;
+    	friend class SwapchainManager;
 
         struct Surface
         {
@@ -20,6 +22,7 @@ namespace vkcv
             std::vector<vk::SurfaceFormatKHR> formats;
             vk::SurfaceCapabilitiesKHR capabilities;
             std::vector<vk::PresentModeKHR> presentModes;
+			uint32_t presentQueueIndex;
         };
         
         Surface m_Surface;
@@ -34,15 +37,18 @@ namespace vkcv
 	
 		std::atomic<bool> m_RecreationRequired;
 
-        /**
-         * Constructor of a SwapChain object
-         * glfw is not initialized in this class because ist must be sure that there exists a context first
-         * glfw is already initialized by the window class
-         * @param surface used by the swapchain
-         * @param swapchain to show images in the window
-         * @param format
-         */
-         // TODO:
+		/**
+		 * Constructor of a SwapChain object
+		 * glfw is not initialized in this class because ist must be sure that there exists a context first
+		 * glfw is already initialized by the window class
+		 * @param surface used by the swapchain
+		 * @param swapchain to show images in the window
+		 * @param format of the swapchain
+		 * @param colorSpace of the swapchain
+		 * @param presentMode of the swapchain
+		 * @param imageCount of the swapchain
+		 * @param extent of the swapchain
+		 */
         Swapchain(const Surface &surface,
                   vk::SwapchainKHR swapchain,
                   vk::Format format,
@@ -52,22 +58,20 @@ namespace vkcv
 				  vk::Extent2D extent) noexcept;
 	
 		/**
-		 * TODO
-		 *
-		 * @return
+		 * checks if the update flag is true
+		 * @return if an update is needed
 		 */
 		bool shouldUpdateSwapchain() const;
 	
 		/**
-		 * TODO
-		 *
+		 * recreates the swapchain
 		 * context
 		 * window
 		 */
 		void updateSwapchain(const Context &context, const Window &window);
 	
 		/**
-		 *
+		 * signal that the swapchain needs to be recreated
 		 */
 		void signalSwapchainRecreation();
 
@@ -114,13 +118,17 @@ namespace vkcv
 		uint32_t getImageCount() const;
 	
         /**
-         * TODO
-         *
-         * @return
+         * @return the 2d extent of the swapchain
          */
         [[nodiscard]]
 		const vk::Extent2D& getExtent() const;
-    
-    };
+
+		/**
+		 * @return the familyQueueIndex for the surface
+		 */
+		[[nodiscard]]
+		const uint32_t& getPresentQueueIndex() const;
+
+	};
     
 }
diff --git a/include/vkcv/Window.hpp b/include/vkcv/Window.hpp
index f3b3a8fe88ae6e8791d7d92361ad5b6bf2447dcb..cef735632689cdd119b694674e9ac7af67471a3c 100644
--- a/include/vkcv/Window.hpp
+++ b/include/vkcv/Window.hpp
@@ -1,107 +1,90 @@
 #pragma once
-/**
- * @authors Sebastian Gaida
- * @file src/vkcv/Window.hpp
- * @brief Window class to handle a basic rendering surface and input
- */
 
 #define NOMINMAX
 #include <algorithm>
+#include <string>
 
 #include "Event.hpp"
+#include "Handles.hpp"
 
 struct GLFWwindow;
 
 namespace vkcv {
 
     class Window {
-	protected:
-		GLFWwindow *m_window;
-	
-		/**
-         *
-         * @param GLFWwindow of the class
-         */
-		explicit Window(GLFWwindow *window);
+		friend class WindowManager;
+		friend class SwapchainManager;
 		
-    private:
-        /**
-         * mouse callback for moving the mouse on the screen
-         * @param[in] window The window that received the event.
-         * @param[in] xpos The new cursor x-coordinate, relative to the left edge of the content area.
-         * @param[in] ypos The new cursor y-coordinate, relative to the top edge of the content area.
-         */
-        static void onMouseMoveEvent(GLFWwindow *window, double x, double y);
-
-        /**
-         * mouseButton callback for mouse buttons
-         * @param[in] button The [mouse button](@ref buttons) that was pressed or released.
-         * @param[in] action One of `GLFW_PRESS` or `GLFW_RELEASE`.  Future releases may add more actions.
-         * @param[in] mods Bit field describing which [modifier keys](@ref mods) were held down.
-         */
-        static void onMouseButtonEvent(GLFWwindow *callbackWindow, int button, int action, int mods);
-
-        /**
-         * @brief A callback function for handling mouse scrolling events.
-         * @param[in] callbackWindow The window that received the event.
-         * @param[in] xoffset The extent of horizontal scrolling.
-         * @param[in] yoffset The extent of vertical scrolling.
-         */
-        static void onMouseScrollEvent(GLFWwindow *callbackWindow, double xoffset, double yoffset);
-
-        /**
-         * resize callback for the resize option of the window
-         * @param[in] window The window that was resized.
-         * @param[in] width The new width, in screen coordinates, of the window.
-         * @param[in] height The new height, in screen coordinates, of the window.
-         */
-        static void onResize(GLFWwindow *callbackWindow, int width, int height);
-
-        /**
-         * key callback for the pressed key
-         * @param[in] window The window that received the event.
-         * @param[in] key The [keyboard key](@ref keys) that was pressed or released.
-         * @param[in] scancode The system-specific scancode of the key.
-         * @param[in] action `GLFW_PRESS`, `GLFW_RELEASE` or `GLFW_REPEAT`.
-         * @param[in] mods Bit field describing which [modifier keys](@ref mods) were held down.
-         */
-        static void onKeyEvent(GLFWwindow *callbackWindow, int key, int scancode, int action, int mods);
-	
-        /**
-         * char callback for any typed character
-         * @param[in] window The window that received the event
-         * @param[in] c The character that got typed
-         */
-		static void onCharEvent(GLFWwindow *callbackWindow, unsigned int c);
-
-        /**
-         * @brief A callback function for gamepad input events.
-         * @param gamepadIndex The gamepad index.
-         */
-        static void onGamepadEvent(int gamepadIndex);
+	private:
+    	std::string m_title;
+    	bool m_resizable;
+		bool m_shouldClose;
+		GLFWwindow *m_window;
+		SwapchainHandle m_swapchainHandle;
+		event_handle<int, int> m_resizeHandle;
 
     public:
-        /**
-         * creates a GLFWwindow with the parameters in the function
-         * @param[in] windowTitle of the window
-         * @param[in] width of the window (optional)
-         * @param[in] height of the window (optional)
+    	/**
+    	 * creates an uninitialized #Window
+    	 */
+    	Window();
+    	
+    	/**
+    	 * creates a #Window with the parameters
+    	 *
+         * @param[in] title title of the window
+         * @param[in] width width of the window (optional)
+         * @param[in] height height of the window (optional)
          * @param[in] resizable resize ability of the window (optional)
-         * @return Window class
          */
-        static Window create( const char *windowTitle, int width = -1, int height = -1, bool resizable = false);
+    	explicit Window(const char* title, int width = -1, int height = -1, bool resizable = false);
+	
+		/**
+		* Copy-constructor of #Window
+		*
+		* @param other Other instance of #Window
+		*/
+		Window(const Window& other) = delete;
+	
+		/**
+		* Copy-operator of #Window
+		*
+		* @param other Other instance of #Window
+		* @return Reference to itself
+		*/
+		Window &operator=(const Window &other) = delete;
+        
         /**
          * checks if the window is still open, or the close event was called
          * This function should be changed/removed later on
          * @return bool if the window is still open
          */
         [[nodiscard]]
-        bool isWindowOpen() const;
+        bool isOpen() const;
+
+		/**
+		 * gets the currently focused window and returns it
+		 * only accessible to WindowManager
+		 * @return
+		 */
+		static Window& getFocusedWindow();
+		
+		/**
+		 * checks if any GLFWWindows are open
+		 * @return bool if a window is open
+		 */
+		static bool hasOpenWindow();
 
         /**
          * polls all events on the GLFWwindow
          */
         static void pollEvents();
+		
+		/**
+		 *
+		 * @return
+		 */
+		static const std::vector<std::string>& getExtensions();
 
         /**
          * basic events of the window
@@ -120,26 +103,16 @@ namespace vkcv {
          */
         [[nodiscard]]
         GLFWwindow *getWindow() const;
-
-        /**
-         * Copy-operator of #Window is deleted!
-         *
-         * @param other Other instance of #Window
-         * @return Reference to itself
-         */
-        Window &operator=(const Window &other) = delete;
-
+        
         /**
-         * Move-operator of #Window uses default behavior!
-         *
-         * @param other Other instance of #Window
-         * @return Reference to itself
+         * gets the window title
+         * @return string with window title
          */
-        Window &operator=(Window &&other) = default;
+        [[nodiscard]]
+        const std::string& getTitle() const;
 
         /**
          * gets the window width
-         * @param window glfwWindow
          * @return int with window width
          */
         [[nodiscard]]
@@ -147,23 +120,40 @@ namespace vkcv {
 
         /**
          * gets the window height
-         * @param window glfwWindow
          * @return int with window height
          */
         [[nodiscard]]
         int getHeight() const;
+        
+        /**
+         * is the window resizable
+         * @return bool with window resizable
+         */
+        [[nodiscard]]
+        bool isResizable() const;
 
         /**
          * Destructor of #Window, terminates GLFW
          */
         virtual ~Window();
-        
+
+        /**
+         * destroys the window
+         */
+		//void destroyWindow();
+
         /**
          * gets the windows framebuffer size
          * @param width
          * @param height
          */
         void getFramebufferSize(int& width, int& height) const;
+
+        /**
+         * gets the SwapchainHandle corresponding to the swapchain of the window
+         * @return
+         */
+        SwapchainHandle getSwapchainHandle() const;
     };
 
 }
diff --git a/modules/gui/include/vkcv/gui/GUI.hpp b/modules/gui/include/vkcv/gui/GUI.hpp
index d1a9986c5f69bfd9d4392bd5ae50f0b1f8b60642..8826cdbac057e39f2655844bfe9a252da43a4596 100644
--- a/modules/gui/include/vkcv/gui/GUI.hpp
+++ b/modules/gui/include/vkcv/gui/GUI.hpp
@@ -11,7 +11,7 @@ namespace vkcv::gui {
 
 	class GUI final {
 	private:
-		Window& m_window;
+		WindowHandle m_windowHandle;
 		Core& m_core;
 		
 		const Context& m_context;
@@ -33,7 +33,7 @@ namespace vkcv::gui {
 		 * @param core Valid #Core instance of the framework
 		 * @param window Valid #Window instance of the framework
 		 */
-		GUI(Core& core, Window& window);
+		GUI(Core& core, WindowHandle& windowHandle);
 		
 		GUI(const GUI& other) = delete;
 		GUI(GUI&& other) = delete;
diff --git a/modules/gui/src/vkcv/gui/GUI.cpp b/modules/gui/src/vkcv/gui/GUI.cpp
index 38bb6894fb2b40c6ab10445f19431f87f7370afc..22c40d2937c69525c04ffd79f26107f829e42f4d 100644
--- a/modules/gui/src/vkcv/gui/GUI.cpp
+++ b/modules/gui/src/vkcv/gui/GUI.cpp
@@ -15,31 +15,33 @@ namespace vkcv::gui {
 		vkcv_log(LogLevel::ERROR, "ImGui has a problem with Vulkan! (%s)", vk::to_string(result).c_str());
 	}
 	
-	GUI::GUI(Core& core, Window& window) :
-	m_window(window),
+	GUI::GUI(Core& core, WindowHandle& windowHandle) :
+	m_windowHandle(windowHandle),
 	m_core(core),
 	m_context(m_core.getContext()),
 	m_gui_context(nullptr) {
 		IMGUI_CHECKVERSION();
 		
 		m_gui_context = ImGui::CreateContext();
+
+		Window& window = m_core.getWindow(windowHandle);
+
+		ImGui_ImplGlfw_InitForVulkan(window.getWindow(), false);
 		
-		ImGui_ImplGlfw_InitForVulkan(m_window.getWindow(), false);
-		
-		f_mouseButton = m_window.e_mouseButton.add([&](int button, int action, int mods) {
-			ImGui_ImplGlfw_MouseButtonCallback(m_window.getWindow(), button, action, mods);
+		f_mouseButton = window.e_mouseButton.add([&](int button, int action, int mods) {
+			ImGui_ImplGlfw_MouseButtonCallback(window.getWindow(), button, action, mods);
 		});
 		
-		f_mouseScroll = m_window.e_mouseScroll.add([&](double xoffset, double yoffset) {
-			ImGui_ImplGlfw_ScrollCallback(m_window.getWindow(), xoffset, yoffset);
+		f_mouseScroll = window.e_mouseScroll.add([&](double xoffset, double yoffset) {
+			ImGui_ImplGlfw_ScrollCallback(window.getWindow(), xoffset, yoffset);
 		});
 		
-		f_key = m_window.e_key.add([&](int key, int scancode, int action, int mods) {
-			ImGui_ImplGlfw_KeyCallback(m_window.getWindow(), key, scancode, action, mods);
+		f_key = window.e_key.add([&](int key, int scancode, int action, int mods) {
+			ImGui_ImplGlfw_KeyCallback(window.getWindow(), key, scancode, action, mods);
 		});
 		
-		f_char = m_window.e_char.add([&](unsigned int c) {
-			ImGui_ImplGlfw_CharCallback(m_window.getWindow(), c);
+		f_char = window.e_char.add([&](unsigned int c) {
+			ImGui_ImplGlfw_CharCallback(window.getWindow(), c);
 		});
 		
 		vk::DescriptorPoolSize pool_sizes[] = {
@@ -66,7 +68,7 @@ namespace vkcv::gui {
 		m_descriptor_pool = m_context.getDevice().createDescriptorPool(descriptorPoolCreateInfo);
 		
 		const vk::PhysicalDevice& physicalDevice = m_context.getPhysicalDevice();
-		const Swapchain& swapchain = m_core.getSwapchain();
+		const Swapchain& swapchain = m_core.getSwapchain(m_windowHandle);
 		
 		const uint32_t graphicsQueueFamilyIndex = (
 				m_context.getQueueManager().getGraphicsQueues()[0].familyIndex
@@ -152,18 +154,19 @@ namespace vkcv::gui {
 	
 	GUI::~GUI() {
 		m_context.getDevice().waitIdle();
-		
+		Window& window = m_core.getWindow(m_windowHandle);
+
 		ImGui_ImplVulkan_Shutdown();
 		
 		m_context.getDevice().destroyRenderPass(m_render_pass);
 		m_context.getDevice().destroyDescriptorPool(m_descriptor_pool);
 		
 		ImGui_ImplGlfw_Shutdown();
-		
-		m_window.e_mouseButton.remove(f_mouseButton);
-		m_window.e_mouseScroll.remove(f_mouseScroll);
-		m_window.e_key.remove(f_key);
-		m_window.e_char.remove(f_char);
+
+		window.e_mouseButton.remove(f_mouseButton);
+		window.e_mouseScroll.remove(f_mouseScroll);
+		window.e_key.remove(f_key);
+		window.e_char.remove(f_char);
 		
 		if (m_gui_context) {
 			ImGui::DestroyContext(m_gui_context);
@@ -171,7 +174,7 @@ namespace vkcv::gui {
 	}
 	
 	void GUI::beginGUI() {
-		const Swapchain& swapchain = m_core.getSwapchain();
+		const Swapchain& swapchain = m_core.getSwapchain(m_windowHandle);
 		const auto extent = swapchain.getExtent();
 		
 		if ((extent.width > 0) && (extent.height > 0)) {
@@ -194,7 +197,7 @@ namespace vkcv::gui {
 			return;
 		}
 		
-		const Swapchain& swapchain = m_core.getSwapchain();
+		const Swapchain& swapchain = m_core.getSwapchain(m_windowHandle);
 		const auto extent = swapchain.getExtent();
 		
 		const vk::ImageView swapchainImageView = m_core.getSwapchainImageView();
diff --git a/modules/scene/include/vkcv/scene/Scene.hpp b/modules/scene/include/vkcv/scene/Scene.hpp
index b0afec4819105e4a27f7e6a635cb9ca70b0db2c5..095c5e278e9e3f76544e884bdb87cea10770307a 100644
--- a/modules/scene/include/vkcv/scene/Scene.hpp
+++ b/modules/scene/include/vkcv/scene/Scene.hpp
@@ -61,7 +61,8 @@ namespace vkcv::scene {
 							 const GraphicsPipelineHandle     &pipeline,
 							 size_t							  pushConstantsSizePerDrawcall,
 							 const RecordMeshDrawcallFunction &record,
-							 const std::vector<ImageHandle>   &renderTargets);
+							 const std::vector<ImageHandle>   &renderTargets,
+							 const WindowHandle               &windowHandle);
 		
 		static Scene create(Core& core);
 		
diff --git a/modules/scene/src/vkcv/scene/Scene.cpp b/modules/scene/src/vkcv/scene/Scene.cpp
index 0cb4efd2c50e6d013f817949241476e848b4a151..cd02004f56ea3dfddcd20618b7859a0699d92d71 100644
--- a/modules/scene/src/vkcv/scene/Scene.cpp
+++ b/modules/scene/src/vkcv/scene/Scene.cpp
@@ -115,7 +115,8 @@ namespace vkcv::scene {
 								const GraphicsPipelineHandle     &pipeline,
 								size_t							 pushConstantsSizePerDrawcall,
 								const RecordMeshDrawcallFunction &record,
-								const std::vector<ImageHandle>   &renderTargets) {
+								const std::vector<ImageHandle>   &renderTargets,
+								const WindowHandle               &windowHandle) {
 		m_core->recordBeginDebugLabel(cmdStream, "vkcv::scene::Scene", {
 			0.0f, 1.0f, 0.0f, 1.0f
 		});
@@ -139,7 +140,8 @@ namespace vkcv::scene {
 				pipeline,
 				pushConstants,
 				drawcalls,
-				renderTargets
+				renderTargets,
+				windowHandle
 		);
 		
 		m_core->recordEndDebugLabel(cmdStream);
diff --git a/projects/first_mesh/src/main.cpp b/projects/first_mesh/src/main.cpp
index c1bce3dbd6315cd7014f4dcfb1df821f871c58f3..5ef277dfedbfa823a2b6fa55c5a6303117ddaa52 100644
--- a/projects/first_mesh/src/main.cpp
+++ b/projects/first_mesh/src/main.cpp
@@ -1,6 +1,5 @@
 #include <iostream>
 #include <vkcv/Core.hpp>
-#include <GLFW/glfw3.h>
 #include <vkcv/camera/CameraManager.hpp>
 #include <chrono>
 #include <vkcv/asset/asset_loader.hpp>
@@ -9,21 +8,18 @@
 int main(int argc, const char** argv) {
 	const char* applicationName = "First Mesh";
 
-	vkcv::Window window = vkcv::Window::create(
-		applicationName,
-		800,
-		600,
-		true
-	);
-	
+	uint32_t windowWidth = 800;
+	uint32_t windowHeight = 600;
+
 	vkcv::Core core = vkcv::Core::create(
-		window,
 		applicationName,
 		VK_MAKE_VERSION(0, 0, 1),
 		{ vk::QueueFlagBits::eGraphics ,vk::QueueFlagBits::eCompute , vk::QueueFlagBits::eTransfer },
 		{ VK_KHR_SWAPCHAIN_EXTENSION_NAME }
 	);
 
+	vkcv::WindowHandle windowHandle = core.createWindow(applicationName, windowWidth, windowHeight, false);
+
 	vkcv::asset::Scene mesh;
 
 	const char* path = argc > 1 ? argv[1] : "assets/cube/cube.gltf";
@@ -57,7 +53,7 @@ int main(int argc, const char** argv) {
 	const vkcv::AttachmentDescription present_color_attachment(
 		vkcv::AttachmentOperation::STORE,
 		vkcv::AttachmentOperation::CLEAR,
-		core.getSwapchain().getFormat()
+		core.getSwapchain(windowHandle).getFormat()
 	);
 	
 	const vkcv::AttachmentDescription depth_attachment(
@@ -158,7 +154,7 @@ int main(int argc, const char** argv) {
 
 	core.writeDescriptorSet(descriptorSet, setWrites);
 	
-	auto swapchainExtent = core.getSwapchain().getExtent();
+	auto swapchainExtent = core.getSwapchain(windowHandle).getExtent();
 	
 	vkcv::ImageHandle depthBuffer = core.createImage(
 			vk::Format::eD32Sfloat,
@@ -174,21 +170,21 @@ int main(int argc, const char** argv) {
 	vkcv::DescriptorSetUsage    descriptorUsage(0, core.getDescriptorSet(descriptorSet).vulkanHandle);
 	vkcv::DrawcallInfo          drawcall(renderMesh, { descriptorUsage },1);
 
-    vkcv::camera::CameraManager cameraManager(window);
+    vkcv::camera::CameraManager cameraManager(core.getWindow(windowHandle));
     uint32_t camIndex0 = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT);
 	
 	cameraManager.getCamera(camIndex0).setPosition(glm::vec3(0, 0, -3));
 
     auto start = std::chrono::system_clock::now();
     
-	while (window.isWindowOpen()) {
+	while (vkcv::Window::hasOpenWindow()) {
         vkcv::Window::pollEvents();
 		
-		if(window.getHeight() == 0 || window.getWidth() == 0)
+		if(core.getWindow(windowHandle).getHeight() == 0 || core.getWindow(windowHandle).getWidth() == 0)
 			continue;
 		
 		uint32_t swapchainWidth, swapchainHeight;
-		if (!core.beginFrame(swapchainWidth, swapchainHeight)) {
+		if (!core.beginFrame(swapchainWidth, swapchainHeight, windowHandle)) {
 			continue;
 		}
 		
@@ -218,10 +214,11 @@ int main(int argc, const char** argv) {
 			firstMeshPipeline,
 			pushConstants,
 			{ drawcall },
-			renderTargets);
+			renderTargets,
+			windowHandle);
 		core.prepareSwapchainImageForPresent(cmdStream);
 		core.submitCommandStream(cmdStream);
-		core.endFrame();
+		core.endFrame(windowHandle);
 	}
 	
 	return 0;
diff --git a/projects/first_scene/src/main.cpp b/projects/first_scene/src/main.cpp
index c7d4fcac5aa1748b9407b003eba3f35f418169f3..4891fecbf8e0fc5dee3f9930b477aa03115916d2 100644
--- a/projects/first_scene/src/main.cpp
+++ b/projects/first_scene/src/main.cpp
@@ -10,14 +10,19 @@
 int main(int argc, const char** argv) {
 	const char* applicationName = "First Scene";
 
-	vkcv::Window window = vkcv::Window::create(
-		applicationName,
-		800,
-		600,
-		true
-	);
+	uint32_t windowWidth = 800;
+	uint32_t windowHeight = 600;
 
+	vkcv::Core core = vkcv::Core::create(
+			applicationName,
+			VK_MAKE_VERSION(0, 0, 1),
+			{vk::QueueFlagBits::eTransfer, vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute},
+			{ VK_KHR_SWAPCHAIN_EXTENSION_NAME }
+	);
+	vkcv::WindowHandle windowHandle = core.createWindow(applicationName, windowWidth, windowHeight, false);
+	vkcv::Window& window = core.getWindow(windowHandle);
 	vkcv::camera::CameraManager cameraManager(window);
+
 	uint32_t camIndex0 = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT);
 	uint32_t camIndex1 = cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL);
 	
@@ -25,15 +30,7 @@ int main(int argc, const char** argv) {
 	cameraManager.getCamera(camIndex0).setNearFar(0.1f, 30.0f);
 	
 	cameraManager.getCamera(camIndex1).setNearFar(0.1f, 30.0f);
-	
-	vkcv::Core core = vkcv::Core::create(
-		window,
-		applicationName,
-		VK_MAKE_VERSION(0, 0, 1),
-		{ vk::QueueFlagBits::eGraphics ,vk::QueueFlagBits::eCompute , vk::QueueFlagBits::eTransfer },
-		{ VK_KHR_SWAPCHAIN_EXTENSION_NAME }
-	);
-	
+
 	vkcv::scene::Scene scene = vkcv::scene::Scene::load(core, std::filesystem::path(
 			argc > 1 ? argv[1] : "assets/Sponza/Sponza.gltf"
 	));
@@ -41,7 +38,7 @@ int main(int argc, const char** argv) {
 	const vkcv::AttachmentDescription present_color_attachment(
 		vkcv::AttachmentOperation::STORE,
 		vkcv::AttachmentOperation::CLEAR,
-		core.getSwapchain().getFormat()
+		core.getSwapchain(windowHandle).getFormat()
 	);
 
 	const vkcv::AttachmentDescription depth_attachment(
@@ -96,7 +93,7 @@ int main(int argc, const char** argv) {
 		return EXIT_FAILURE;
 	}
 	
-	auto swapchainExtent = core.getSwapchain().getExtent();
+	auto swapchainExtent = core.getSwapchain(windowHandle).getExtent();
 
 	vkcv::ImageHandle depthBuffer = core.createImage(
 			vk::Format::eD32Sfloat,
@@ -107,14 +104,14 @@ int main(int argc, const char** argv) {
 	const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle();
 	
 	auto start = std::chrono::system_clock::now();
-	while (window.isWindowOpen()) {
+	while (vkcv::Window::hasOpenWindow()) {
         vkcv::Window::pollEvents();
 		
 		if(window.getHeight() == 0 || window.getWidth() == 0)
 			continue;
 		
 		uint32_t swapchainWidth, swapchainHeight;
-		if (!core.beginFrame(swapchainWidth, swapchainHeight)) {
+		if (!core.beginFrame(swapchainWidth, swapchainHeight,windowHandle)) {
 			continue;
 		}
 		
@@ -146,11 +143,12 @@ int main(int argc, const char** argv) {
 							  scenePipeline,
 							  sizeof(glm::mat4),
 							  recordMesh,
-							  renderTargets);
+							  renderTargets,
+							  windowHandle);
 		
 		core.prepareSwapchainImageForPresent(cmdStream);
 		core.submitCommandStream(cmdStream);
-		core.endFrame();
+		core.endFrame(windowHandle);
 	}
 	
 	return 0;
diff --git a/projects/first_triangle/src/main.cpp b/projects/first_triangle/src/main.cpp
index b083af4a9f4578ea62888d3be9139928204d67ac..1725b5c84dea931fe785880e3cd423cbb5f04a46 100644
--- a/projects/first_triangle/src/main.cpp
+++ b/projects/first_triangle/src/main.cpp
@@ -7,22 +7,20 @@
 
 int main(int argc, const char** argv) {
 	const char* applicationName = "First Triangle";
-	
-	vkcv::Window window = vkcv::Window::create(
-		applicationName,
-		800,
-		600,
-		false
-	);
+
+	const int windowWidth = 800;
+	const int windowHeight = 600;
 	
 	vkcv::Core core = vkcv::Core::create(
-		window,
 		applicationName,
 		VK_MAKE_VERSION(0, 0, 1),
 		{ vk::QueueFlagBits::eTransfer,vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute },
 		{ VK_KHR_SWAPCHAIN_EXTENSION_NAME }
 	);
 
+	vkcv::WindowHandle windowHandle = core.createWindow(applicationName, windowWidth, windowHeight, true);
+	vkcv::Window& window = core.getWindow(windowHandle);
+
 	auto triangleIndexBuffer = core.createBuffer<uint16_t>(vkcv::BufferType::INDEX, 3, vkcv::BufferMemoryType::DEVICE_LOCAL);
 	uint16_t indices[3] = { 0, 1, 2 };
 	triangleIndexBuffer.fill(&indices[0], sizeof(indices));
@@ -33,7 +31,7 @@ int main(int argc, const char** argv) {
 	const vkcv::AttachmentDescription present_color_attachment(
 		vkcv::AttachmentOperation::STORE,
 		vkcv::AttachmentOperation::CLEAR,
-		core.getSwapchain().getFormat());
+		core.getSwapchain(windowHandle).getFormat());
 
 	vkcv::PassConfig trianglePassDefinition({ present_color_attachment });
 	vkcv::PassHandle trianglePass = core.createPass(trianglePassDefinition);
@@ -59,7 +57,7 @@ int main(int argc, const char** argv) {
 		triangleShaderProgram.addShader(shaderStage, path);
 	});
 	
-	const auto swapchainExtent = core.getSwapchain().getExtent();
+	const auto swapchainExtent = core.getSwapchain(windowHandle).getExtent();
 
 	const vkcv::GraphicsPipelineConfig trianglePipelineDefinition {
 		triangleShaderProgram,
@@ -97,12 +95,12 @@ int main(int argc, const char** argv) {
     cameraManager.getCamera(camIndex1).setPosition(glm::vec3(0.0f, 0.0f, 0.0f));
     cameraManager.getCamera(camIndex1).setCenter(glm::vec3(0.0f, 0.0f, -1.0f));
 
-	while (window.isWindowOpen())
+	while (vkcv::Window::hasOpenWindow())
 	{
         vkcv::Window::pollEvents();
 
 		uint32_t swapchainWidth, swapchainHeight; // No resizing = No problem
-		if (!core.beginFrame(swapchainWidth, swapchainHeight)) {
+		if (!core.beginFrame(swapchainWidth, swapchainHeight, windowHandle)) {
 			continue;
 		}
 		
@@ -125,12 +123,13 @@ int main(int argc, const char** argv) {
 			trianglePipeline,
 			pushConstants,
 			{ drawcall },
-			{ swapchainInput });
+			{ swapchainInput },
+			windowHandle);
 
 		core.prepareSwapchainImageForPresent(cmdStream);
 		core.submitCommandStream(cmdStream);
 	    
-	    core.endFrame();
+	    core.endFrame(windowHandle);
 	}
 	return 0;
 }
diff --git a/projects/indirect_dispatch/src/App.cpp b/projects/indirect_dispatch/src/App.cpp
index ac7ddf52a4ef60254ac5c265be178ab927abe934..d4afc61c7421bd45c773bbdbc3da796b868869d2 100644
--- a/projects/indirect_dispatch/src/App.cpp
+++ b/projects/indirect_dispatch/src/App.cpp
@@ -23,18 +23,13 @@ App::App() :
 	m_applicationName("Indirect Dispatch"),
 	m_windowWidth(AppConfig::defaultWindowWidth),
 	m_windowHeight(AppConfig::defaultWindowHeight),
-	m_window(vkcv::Window::create(
-		m_applicationName,
-		m_windowWidth,
-		m_windowHeight,
-		true)),
 	m_core(vkcv::Core::create(
-		m_window,
 		m_applicationName,
 		VK_MAKE_VERSION(0, 0, 1),
 		{ vk::QueueFlagBits::eGraphics ,vk::QueueFlagBits::eCompute , vk::QueueFlagBits::eTransfer },
 		{ VK_KHR_SWAPCHAIN_EXTENSION_NAME })),
-	m_cameraManager(m_window){}
+	m_windowHandle(m_core.createWindow(m_applicationName, m_windowWidth, m_windowHeight, false)),
+	m_cameraManager(m_core.getWindow(m_windowHandle)){}
 
 bool App::initialize() {
 
@@ -92,7 +87,7 @@ void App::run() {
 	const vkcv::ImageHandle     swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle();
 	const vkcv::DrawcallInfo    skyDrawcall(m_cubeMesh.mesh, {}, 1);
 
-	vkcv::gui::GUI gui(m_core, m_window);
+	vkcv::gui::GUI gui(m_core, m_windowHandle);
 
 	eMotionVectorVisualisationMode  motionVectorVisualisationMode   = eMotionVectorVisualisationMode::None;
 	eMotionBlurMode                 motionBlurMode                  = eMotionBlurMode::Default;
@@ -137,7 +132,7 @@ void App::run() {
 
 	bool spaceWasPressed = false;
 
-	m_window.e_key.add([&](int key, int scancode, int action, int mods) {
+	m_core.getWindow(m_windowHandle).e_key.add([&](int key, int scancode, int action, int mods) {
 		if (key == GLFW_KEY_SPACE) {
 			if (action == GLFW_PRESS) {
 				if (!spaceWasPressed) {
@@ -153,7 +148,7 @@ void App::run() {
 
 	auto frameEndTime = std::chrono::system_clock::now();
 
-	while (m_window.isWindowOpen()) {
+	while (vkcv::Window::hasOpenWindow()) {
 
 		vkcv::Window::pollEvents();
 
@@ -167,11 +162,11 @@ void App::run() {
 			}
 		}
 
-		if (m_window.getHeight() == 0 || m_window.getWidth() == 0)
+		if (m_core.getWindow(m_windowHandle).getHeight() == 0 || m_core.getWindow(m_windowHandle).getWidth() == 0)
 			continue;
 
 		uint32_t swapchainWidth, swapchainHeight;
-		if (!m_core.beginFrame(swapchainWidth, swapchainHeight))
+		if (!m_core.beginFrame(swapchainWidth, swapchainHeight,m_windowHandle))
 			continue;
 
 		const bool hasResolutionChanged = (swapchainWidth != m_windowWidth) || (swapchainHeight != m_windowHeight);
@@ -232,7 +227,8 @@ void App::run() {
 			m_prePass.pipeline,
 			prepassPushConstants,
 			prepassSceneDrawcalls,
-			prepassRenderTargets);
+			prepassRenderTargets,
+			m_windowHandle);
 
 		// sky prepass
 		glm::mat4 skyPrepassMatrices[2] = {
@@ -247,7 +243,8 @@ void App::run() {
 			m_skyPrePass.pipeline,
 			skyPrepassPushConstants,
 			{ skyDrawcall },
-			prepassRenderTargets);
+			prepassRenderTargets,
+			m_windowHandle);
 
 		// main pass
 		const std::vector<vkcv::ImageHandle> renderTargets   = { 
@@ -273,7 +270,8 @@ void App::run() {
 			m_meshPass.pipeline,
 			meshPushConstants,
 			forwardSceneDrawcalls,
-			renderTargets);
+			renderTargets,
+			m_windowHandle);
 
 		// sky
 		vkcv::PushConstants skyPushConstants(sizeof(glm::mat4));
@@ -285,7 +283,8 @@ void App::run() {
 			m_skyPass.pipeline,
 			skyPushConstants,
 			{ skyDrawcall },
-			renderTargets);
+			renderTargets,
+			m_windowHandle);
 
 		// motion blur
 		vkcv::ImageHandle motionBlurOutput;
@@ -378,6 +377,6 @@ void App::run() {
 		ImGui::End();
 		gui.endGUI();
 
-		m_core.endFrame();
+		m_core.endFrame(m_windowHandle);
 	}
 }
\ No newline at end of file
diff --git a/projects/indirect_dispatch/src/App.hpp b/projects/indirect_dispatch/src/App.hpp
index 4ce66e9b28d44024cee0d07f6f765d396b873bd7..a35c2342c4c90e39b089a7c33a73c3aa7ce8a83f 100644
--- a/projects/indirect_dispatch/src/App.hpp
+++ b/projects/indirect_dispatch/src/App.hpp
@@ -15,8 +15,8 @@ private:
 	uint32_t m_windowWidth;
 	uint32_t m_windowHeight;
 
-	vkcv::Window                m_window;
 	vkcv::Core                  m_core;
+	vkcv::WindowHandle          m_windowHandle;
 	vkcv::camera::CameraManager m_cameraManager;
 
 	MotionBlur m_motionBlur;
diff --git a/projects/mesh_shader/src/main.cpp b/projects/mesh_shader/src/main.cpp
index 6c3bc2f40090c87ae967499e4ff53bf0af81b9ff..0ee16b99822e0c8fbeaf58af57f02255493b0fb5 100644
--- a/projects/mesh_shader/src/main.cpp
+++ b/projects/mesh_shader/src/main.cpp
@@ -78,12 +78,8 @@ CameraPlanes computeCameraPlanes(const vkcv::camera::Camera& camera) {
 int main(int argc, const char** argv) {
 	const char* applicationName = "Mesh shader";
 
-	vkcv::Window window = vkcv::Window::create(
-		applicationName,
-		1280,
-		720,
-		false
-	);
+	const int windowWidth = 1280;
+	const int windowHeight = 720;
 	
 	vkcv::Features features;
 	features.requireExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
@@ -94,14 +90,15 @@ int main(int argc, const char** argv) {
 	});
 
 	vkcv::Core core = vkcv::Core::create(
-		window,
 		applicationName,
 		VK_MAKE_VERSION(0, 0, 1),
 		{ vk::QueueFlagBits::eTransfer,vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute },
 		features
 	);
+	vkcv::WindowHandle windowHandle = core.createWindow(applicationName, windowWidth, windowHeight, false);
+	vkcv::Window &window = core.getWindow(windowHandle);
 
-    vkcv::gui::GUI gui (core, window);
+    vkcv::gui::GUI gui (core, windowHandle);
 
     vkcv::asset::Scene mesh;
     const char* path = argc > 1 ? argv[1] : "assets/Bunny/Bunny.glb";
@@ -166,7 +163,7 @@ int main(int argc, const char** argv) {
 	const vkcv::AttachmentDescription present_color_attachment(
 		vkcv::AttachmentOperation::STORE,
 		vkcv::AttachmentOperation::CLEAR,
-		core.getSwapchain().getFormat());
+		core.getSwapchain(windowHandle).getFormat());
 
     const vkcv::AttachmentDescription depth_attachment(
             vkcv::AttachmentOperation::STORE,
@@ -206,7 +203,7 @@ int main(int argc, const char** argv) {
     vkcv::DescriptorSetLayoutHandle vertexShaderDescriptorSetLayout = core.createDescriptorSetLayout(bunnyShaderProgram.getReflectedDescriptors().at(0));
     vkcv::DescriptorSetHandle vertexShaderDescriptorSet = core.createDescriptorSet(vertexShaderDescriptorSetLayout);
 
-	auto swapchainExtent = core.getSwapchain().getExtent();
+	auto swapchainExtent = core.getSwapchain(windowHandle).getExtent();
 	
 	const vkcv::GraphicsPipelineConfig bunnyPipelineDefinition {
 			bunnyShaderProgram,
@@ -315,12 +312,12 @@ int main(int argc, const char** argv) {
 	bool useMeshShader          = true;
 	bool updateFrustumPlanes    = true;
 
-	while (window.isWindowOpen())
+	while (vkcv::Window::hasOpenWindow())
 	{
 		vkcv::Window::pollEvents();
 
 		uint32_t swapchainWidth, swapchainHeight; // No resizing = No problem
-		if (!core.beginFrame(swapchainWidth, swapchainHeight)) {
+		if (!core.beginFrame(swapchainWidth, swapchainHeight,windowHandle)) {
 			continue;
 		}
 		
@@ -366,7 +363,8 @@ int main(int argc, const char** argv) {
 				meshShaderPipeline,
 				pushConstantData,
 				{ vkcv::MeshShaderDrawcall({descriptorUsage}, taskCount)},
-				{ renderTargets });
+				{ renderTargets },
+				windowHandle);
 		}
 		else {
 
@@ -378,7 +376,8 @@ int main(int argc, const char** argv) {
 				bunnyPipeline,
 				pushConstantData,
 				{ vkcv::DrawcallInfo(renderMesh, { descriptorUsage }) },
-				{ renderTargets });
+				{ renderTargets },
+				windowHandle);
 		}
 
 		core.prepareSwapchainImageForPresent(cmdStream);
@@ -394,7 +393,7 @@ int main(int argc, const char** argv) {
 		
 		gui.endGUI();
 
-		core.endFrame();
+		core.endFrame(windowHandle);
 	}
 	return 0;
 }
diff --git a/projects/particle_simulation/src/main.cpp b/projects/particle_simulation/src/main.cpp
index ff249e4e8efe2ca60c7773d32a66a05d12f06c3a..ddbe6195e66e78504d4bccb32b3b09ff680ab414 100644
--- a/projects/particle_simulation/src/main.cpp
+++ b/projects/particle_simulation/src/main.cpp
@@ -6,29 +6,25 @@
 #include "ParticleSystem.hpp"
 #include <random>
 #include <glm/gtc/matrix_access.hpp>
-#include <time.h>
+#include <ctime>
 #include <vkcv/shader/GLSLCompiler.hpp>
 #include "BloomAndFlares.hpp"
 
 int main(int argc, const char **argv) {
     const char *applicationName = "Particlesystem";
 
-    vkcv::Window window = vkcv::Window::create(
-            applicationName,
-            800,
-            600,
-            true
-    );
-
-    vkcv::camera::CameraManager cameraManager(window);
+    uint32_t windowWidth = 800;
+    uint32_t windowHeight = 600;
 	
     vkcv::Core core = vkcv::Core::create(
-            window,
             applicationName,
             VK_MAKE_VERSION(0, 0, 1),
             {vk::QueueFlagBits::eTransfer, vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute},
 			{ VK_KHR_SWAPCHAIN_EXTENSION_NAME }
     );
+	vkcv::WindowHandle windowHandle = core.createWindow(applicationName, windowWidth, windowHeight, true);
+    vkcv::Window& window = core.getWindow(windowHandle);
+	vkcv::camera::CameraManager cameraManager(window);
 
     auto particleIndexBuffer = core.createBuffer<uint16_t>(vkcv::BufferType::INDEX, 3,
                                                            vkcv::BufferMemoryType::DEVICE_LOCAL);
@@ -186,7 +182,7 @@ int main(int argc, const char **argv) {
     auto pos = glm::vec2(0.f);
     auto spawnPosition = glm::vec3(0.f);
 
-    window.e_mouseMove.add([&](double offsetX, double offsetY) {
+	window.e_mouseMove.add([&](double offsetX, double offsetY) {
         pos = glm::vec2(static_cast<float>(offsetX), static_cast<float>(offsetY));
         pos.x = (-2 * pos.x + static_cast<float>(window.getWidth())) / static_cast<float>(window.getWidth());
         pos.y = (-2 * pos.y + static_cast<float>(window.getHeight())) / static_cast<float>(window.getHeight());
@@ -213,7 +209,7 @@ int main(int argc, const char **argv) {
     cameraManager.getCamera(camIndex1).setPosition(glm::vec3(0.0f, 0.0f, -2.0f));
     cameraManager.getCamera(camIndex1).setCenter(glm::vec3(0.0f, 0.0f, 0.0f));
 
-	auto swapchainExtent = core.getSwapchain().getExtent();
+	auto swapchainExtent = core.getSwapchain(windowHandle).getExtent();
 	
     vkcv::ImageHandle colorBuffer = core.createImage(
 			colorFormat,
@@ -223,7 +219,7 @@ int main(int argc, const char **argv) {
 	).getHandle();
     BloomAndFlares bloomAndFlares(&core, colorFormat, swapchainExtent.width, swapchainExtent.height);
     window.e_resize.add([&](int width, int height) {
-		swapchainExtent = core.getSwapchain().getExtent();
+		swapchainExtent = core.getSwapchain(windowHandle).getExtent();
         colorBuffer = core.createImage(
 				colorFormat,
 				swapchainExtent.width,
@@ -246,11 +242,11 @@ int main(int argc, const char **argv) {
 
     std::uniform_real_distribution<float> rdm = std::uniform_real_distribution<float>(0.95f, 1.05f);
     std::default_random_engine rdmEngine;
-    while (window.isWindowOpen()) {
+    while (vkcv::Window::hasOpenWindow()) {
         vkcv::Window::pollEvents();
 
         uint32_t swapchainWidth, swapchainHeight;
-        if (!core.beginFrame(swapchainWidth, swapchainHeight)) {
+        if (!core.beginFrame(swapchainWidth, swapchainHeight, windowHandle)) {
             continue;
         }
 
@@ -297,7 +293,8 @@ int main(int argc, const char **argv) {
                 particlePipeline,
 				pushConstantsDraw,
                 {drawcalls},
-                { colorBuffer });
+                { colorBuffer },
+                windowHandle);
 
         bloomAndFlares.execWholePipeline(cmdStream, colorBuffer);
 
@@ -325,7 +322,7 @@ int main(int argc, const char **argv) {
 
         core.prepareSwapchainImageForPresent(cmdStream);
         core.submitCommandStream(cmdStream);
-        core.endFrame();
+        core.endFrame(windowHandle);
     }
 
     return 0;
diff --git a/projects/voxelization/src/ShadowMapping.cpp b/projects/voxelization/src/ShadowMapping.cpp
index 55fb781c52049fd18722f263033d78f7e0e9bebd..d8041c1e9935d148ded8fb1ca8f0e4d8b79fce71 100644
--- a/projects/voxelization/src/ShadowMapping.cpp
+++ b/projects/voxelization/src/ShadowMapping.cpp
@@ -235,7 +235,8 @@ void ShadowMapping::recordShadowMapRendering(
 	const std::vector<glm::mat4>&       modelMatrices,
 	const vkcv::camera::Camera&         camera,
 	const glm::vec3&                    voxelVolumeOffset,
-	float                               voxelVolumeExtent) {
+	float                               voxelVolumeExtent,
+	const vkcv::WindowHandle&           windowHandle) {
 
 	LightInfo lightInfo;
 	lightInfo.sunColor = lightColor;
@@ -271,7 +272,8 @@ void ShadowMapping::recordShadowMapRendering(
 		m_shadowMapPipe,
 		shadowPushConstants,
 		drawcalls,
-		{ m_shadowMapDepth.getHandle() });
+		{ m_shadowMapDepth.getHandle() },
+		windowHandle);
 	m_corePtr->prepareImageForSampling(cmdStream, m_shadowMapDepth.getHandle());
 
 	// depth to moments
diff --git a/projects/voxelization/src/ShadowMapping.hpp b/projects/voxelization/src/ShadowMapping.hpp
index 3a577a75c6a374fb41bf02056efe64eef331fcdc..7de83b800ce073f5541a889b87a7f0cd763ffa69 100644
--- a/projects/voxelization/src/ShadowMapping.hpp
+++ b/projects/voxelization/src/ShadowMapping.hpp
@@ -27,7 +27,8 @@ public:
 		const std::vector<glm::mat4>&       modelMatrices,
 		const vkcv::camera::Camera&         camera,
 		const glm::vec3&                    voxelVolumeOffset,
-		float                               voxelVolumeExtent);
+		float                               voxelVolumeExtent,
+		const vkcv::WindowHandle&           windowHandle);
 
 	vkcv::ImageHandle   getShadowMap();
 	vkcv::SamplerHandle getShadowSampler();
diff --git a/projects/voxelization/src/Voxelization.cpp b/projects/voxelization/src/Voxelization.cpp
index 668dc8596951356e97535f09d9a74d8d15ae2003..3cbff0df84757fb370a0372ddd45a9df401d4b60 100644
--- a/projects/voxelization/src/Voxelization.cpp
+++ b/projects/voxelization/src/Voxelization.cpp
@@ -218,7 +218,8 @@ void Voxelization::voxelizeMeshes(
 	vkcv::CommandStreamHandle                       cmdStream,
 	const std::vector<vkcv::Mesh>&                  meshes,
 	const std::vector<glm::mat4>&                   modelMatrices,
-	const std::vector<vkcv::DescriptorSetHandle>&   perMeshDescriptorSets) {
+	const std::vector<vkcv::DescriptorSetHandle>&   perMeshDescriptorSets,
+	const vkcv::WindowHandle&                       windowHandle) {
 
 	m_voxelInfoBuffer.fill({ m_voxelInfo });
 
@@ -276,7 +277,8 @@ void Voxelization::voxelizeMeshes(
 		m_voxelizationPipe,
 		voxelizationPushConstants,
 		drawcalls,
-		{ m_dummyRenderTarget.getHandle() });
+		{ m_dummyRenderTarget.getHandle() },
+		windowHandle);
 
 	// buffer to image
 	const uint32_t bufferToImageGroupSize[3] = { 4, 4, 4 };
@@ -320,7 +322,8 @@ void Voxelization::renderVoxelVisualisation(
 	vkcv::CommandStreamHandle               cmdStream, 
 	const glm::mat4&                        viewProjectin,
 	const std::vector<vkcv::ImageHandle>&   renderTargets,
-	uint32_t                                mipLevel) {
+	uint32_t                                mipLevel,
+	const vkcv::WindowHandle&               windowHandle) {
 
 	vkcv::PushConstants voxelVisualisationPushConstants (sizeof(glm::mat4));
 	voxelVisualisationPushConstants.appendDrawcall(viewProjectin);
@@ -348,7 +351,8 @@ void Voxelization::renderVoxelVisualisation(
 		m_visualisationPipe,
 		voxelVisualisationPushConstants,
 		{ drawcall },
-		renderTargets);
+		renderTargets,
+		windowHandle);
 }
 
 void Voxelization::updateVoxelOffset(const vkcv::camera::Camera& camera) {
diff --git a/projects/voxelization/src/Voxelization.hpp b/projects/voxelization/src/Voxelization.hpp
index c099581a527045a7cb51e4de144f3c181fee53b1..d5f41123b527f2919dec678d0337ee25ea6fa2c6 100644
--- a/projects/voxelization/src/Voxelization.hpp
+++ b/projects/voxelization/src/Voxelization.hpp
@@ -23,13 +23,15 @@ public:
 		vkcv::CommandStreamHandle                       cmdStream,
 		const std::vector<vkcv::Mesh>&                  meshes,
 		const std::vector<glm::mat4>&                   modelMatrices,
-		const std::vector<vkcv::DescriptorSetHandle>&   perMeshDescriptorSets);
+		const std::vector<vkcv::DescriptorSetHandle>&   perMeshDescriptorSets,
+		const vkcv::WindowHandle&                       windowHandle);
 
 	void renderVoxelVisualisation(
 		vkcv::CommandStreamHandle               cmdStream,
 		const glm::mat4&                        viewProjectin,
 		const std::vector<vkcv::ImageHandle>&   renderTargets,
-		uint32_t                                mipLevel);
+		uint32_t                                mipLevel,
+		const vkcv::WindowHandle&               windowHandle);
 
 	void updateVoxelOffset(const vkcv::camera::Camera& camera);
 	void setVoxelExtent(float extent);
diff --git a/projects/voxelization/src/main.cpp b/projects/voxelization/src/main.cpp
index 795acb1795e7d9242fe9e9c4969a8833cb8c1175..a7798813a52429d31f207ae24af6a3effb90c17c 100644
--- a/projects/voxelization/src/main.cpp
+++ b/projects/voxelization/src/main.cpp
@@ -17,13 +17,23 @@ int main(int argc, const char** argv) {
 
 	const vkcv::Multisampling   msaa        = vkcv::Multisampling::MSAA4X;
 	const bool                  usingMsaa   = msaa != vkcv::Multisampling::None;
-	
-	vkcv::Window window = vkcv::Window::create(
-		applicationName,
-		1280,
-		720,
-		true
+
+	vkcv::Features features;
+	features.requireExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
+	features.requireExtension(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME);
+	features.requireExtension(VK_KHR_16BIT_STORAGE_EXTENSION_NAME);
+
+	const uint32_t windowWidth = 1280;
+	const uint32_t windowHeight = 720;
+
+	vkcv::Core core = vkcv::Core::create(
+			applicationName,
+			VK_MAKE_VERSION(0, 0, 1),
+			{ vk::QueueFlagBits::eTransfer,vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute },
+			features
 	);
+	vkcv::WindowHandle windowHandle = core.createWindow(applicationName, windowWidth, windowHeight, true);
+	vkcv::Window& window = core.getWindow(windowHandle);
 
 	bool     isFullscreen            = false;
 	uint32_t windowedWidthBackup     = window.getWidth();
@@ -76,19 +86,6 @@ int main(int argc, const char** argv) {
 	cameraManager.getCamera(camIndex).setFov(glm::radians(37.8));	// fov of a 35mm lens
 	
 	cameraManager.getCamera(camIndex2).setNearFar(0.1f, 30.0f);
-	
-	vkcv::Features features;
-	features.requireExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
-	features.requireExtension(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME);
-	features.requireExtension(VK_KHR_16BIT_STORAGE_EXTENSION_NAME);
-	
-	vkcv::Core core = vkcv::Core::create(
-		window,
-		applicationName,
-		VK_MAKE_VERSION(0, 0, 1),
-		{ vk::QueueFlagBits::eTransfer,vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute },
-		features
-	);
 
 	vkcv::asset::Scene mesh;
 
@@ -301,7 +298,7 @@ int main(int argc, const char** argv) {
 	vkcv::DescriptorSetLayoutHandle prepassDescriptorSetLayout = core.createDescriptorSetLayout({});
 	vkcv::DescriptorSetHandle prepassDescriptorSet = core.createDescriptorSet(prepassDescriptorSetLayout);
 
-	auto swapchainExtent = core.getSwapchain().getExtent();
+	auto swapchainExtent = core.getSwapchain(windowHandle).getExtent();
 	
 	vkcv::GraphicsPipelineConfig prepassPipelineConfig{
 		depthPrepassShader,
@@ -602,7 +599,7 @@ int main(int argc, const char** argv) {
 	
 	bool bilinearUpscaling = false;
 	
-	vkcv::gui::GUI gui(core, window);
+	vkcv::gui::GUI gui(core, windowHandle);
 
 	glm::vec2   lightAnglesDegree               = glm::vec2(90.f, 0.f);
 	glm::vec3   lightColor                      = glm::vec3(1);
@@ -622,11 +619,11 @@ int main(int argc, const char** argv) {
 
 	auto start = std::chrono::system_clock::now();
 	const auto appStartTime = start;
-	while (window.isWindowOpen()) {
+	while (vkcv::Window::hasOpenWindow()) {
 		vkcv::Window::pollEvents();
 
 		uint32_t swapchainWidth, swapchainHeight;
-		if (!core.beginFrame(swapchainWidth, swapchainHeight)) {
+		if (!core.beginFrame(swapchainWidth, swapchainHeight, windowHandle)) {
 			continue;
 		}
 		
@@ -743,7 +740,8 @@ int main(int argc, const char** argv) {
 			modelMatrices,
 			cameraManager.getActiveCamera(),
 			voxelization.getVoxelOffset(),
-			voxelization.getVoxelExtent());
+			voxelization.getVoxelExtent(),
+			windowHandle);
 
 		// voxelization
 		voxelization.setVoxelExtent(voxelizationExtent);
@@ -751,7 +749,8 @@ int main(int argc, const char** argv) {
 			cmdStream,
 			meshes, 
 			modelMatrices,
-			perMeshDescriptorSets);
+			perMeshDescriptorSets,
+			windowHandle);
 
 		// depth prepass
 		const glm::mat4 viewProjectionCamera = cameraManager.getActiveCamera().getMVP();
@@ -772,7 +771,8 @@ int main(int argc, const char** argv) {
 			prepassPipeline,
 			prepassPushConstants,
 			prepassDrawcalls,
-			prepassRenderTargets);
+			prepassRenderTargets,
+			windowHandle);
 
 		core.recordImageMemoryBarrier(cmdStream, depthBuffer);
 		
@@ -797,10 +797,11 @@ int main(int argc, const char** argv) {
 			forwardPipeline,
 			pushConstants,
 			drawcalls,
-			renderTargets);
+			renderTargets,
+			windowHandle);
 
 		if (renderVoxelVis) {
-			voxelization.renderVoxelVisualisation(cmdStream, viewProjectionCamera, renderTargets, voxelVisualisationMip);
+			voxelization.renderVoxelVisualisation(cmdStream, viewProjectionCamera, renderTargets, voxelVisualisationMip, windowHandle);
 		}
 		
 		vkcv::PushConstants skySettingsPushConstants (sizeof(skySettings));
@@ -813,7 +814,8 @@ int main(int argc, const char** argv) {
 			skyPipe,
 			skySettingsPushConstants,
 			{ vkcv::DrawcallInfo(vkcv::Mesh({}, nullptr, 3), {}) },
-			renderTargets);
+			renderTargets,
+			windowHandle);
 
 		const uint32_t fullscreenLocalGroupSize = 8;
 		
@@ -988,7 +990,7 @@ int main(int argc, const char** argv) {
 
 		gui.endGUI();
 
-		core.endFrame();
+		core.endFrame(windowHandle);
 	}
 	
 	return 0;
diff --git a/src/vkcv/Context.cpp b/src/vkcv/Context.cpp
index 512d05e9b1337dcd7d6c5d8162d6155f7a4dd97e..01f6039eba5646bc99780415b7c41f0a02d65fe6 100644
--- a/src/vkcv/Context.cpp
+++ b/src/vkcv/Context.cpp
@@ -1,7 +1,6 @@
 
-#include <GLFW/glfw3.h>
-
 #include "vkcv/Context.hpp"
+#include "vkcv/Window.hpp"
 
 namespace vkcv
 {
@@ -177,13 +176,11 @@ namespace vkcv
 		return true;
 	}
 	
-	std::vector<const char*> getRequiredExtensions() {
-		uint32_t glfwExtensionCount = 0;
-		const char** glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
-		std::vector<const char*> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount);
-
+	std::vector<std::string> getRequiredExtensions() {
+		std::vector<std::string> extensions = Window::getExtensions();
+		
 #ifndef NDEBUG
-		extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
+		extensions.emplace_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
 #endif
 		
 		return extensions;
@@ -227,7 +224,13 @@ namespace vkcv
 		}
 		
 		// for GLFW: get all required extensions
-		std::vector<const char*> requiredExtensions = getRequiredExtensions();
+		auto requiredStrings = getRequiredExtensions();
+		std::vector<const char*> requiredExtensions;
+		
+		for (const auto& extension : requiredStrings) {
+			requiredExtensions.push_back(extension.c_str());
+		}
+		
 		requiredExtensions.insert(requiredExtensions.end(), instanceExtensions.begin(), instanceExtensions.end());
 		
 		if (!checkSupport(supportedExtensions, requiredExtensions)) {
diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp
index e55fb9cb53a001f71e756e0f2d04195e9e237f6d..21435c818451d1bea796b862cbb57ebeea8f0abd 100644
--- a/src/vkcv/Core.cpp
+++ b/src/vkcv/Core.cpp
@@ -14,6 +14,7 @@
 #include "SamplerManager.hpp"
 #include "ImageManager.hpp"
 #include "DescriptorManager.hpp"
+#include "WindowManager.hpp"
 #include "ImageLayoutTransitions.hpp"
 #include "vkcv/CommandStreamManager.hpp"
 #include <cmath>
@@ -21,38 +22,7 @@
 
 namespace vkcv
 {
-	
-	static std::vector<vk::ImageView> createSwapchainImageViews( Context &context, const std::vector<vk::Image>& images,
-																 vk::Format format){
-		std::vector<vk::ImageView> imageViews;
-		imageViews.reserve( images.size() );
-		//here can be swizzled with vk::ComponentSwizzle if needed
-		vk::ComponentMapping componentMapping(
-				vk::ComponentSwizzle::eR,
-				vk::ComponentSwizzle::eG,
-				vk::ComponentSwizzle::eB,
-				vk::ComponentSwizzle::eA );
-		
-		vk::ImageSubresourceRange subResourceRange( vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1 );
-		
-		for ( auto image : images )
-		{
-			vk::ImageViewCreateInfo imageViewCreateInfo(
-					vk::ImageViewCreateFlags(),
-					image,
-					vk::ImageViewType::e2D,
-					format,
-					componentMapping,
-					subResourceRange);
-			
-			imageViews.push_back(context.getDevice().createImageView(imageViewCreateInfo));
-		}
-		
-		return imageViews;
-	}
-
-    Core Core::create(Window &window,
-                      const char *applicationName,
+    Core Core::create(const char *applicationName,
                       uint32_t applicationVersion,
                       const std::vector<vk::QueueFlagBits>& queueFlags,
 					  const Features& features,
@@ -65,34 +35,22 @@ namespace vkcv
         		instanceExtensions
 		);
 
-        Swapchain swapChain = Swapchain::create(window, context);
-	
-		const auto swapchainImages = context.getDevice().getSwapchainImagesKHR(swapChain.getSwapchain());
-		const auto swapchainImageViews = createSwapchainImageViews( context, swapchainImages, swapChain.getFormat());
-
         const auto& queueManager = context.getQueueManager();
         
 		const std::unordered_set<int>	queueFamilySet			= generateQueueFamilyIndexSet(queueManager);
 		const auto						commandResources		= createCommandResources(context.getDevice(), queueFamilySet);
 		const auto						defaultSyncResources	= createSyncResources(context.getDevice());
 
-        return Core(std::move(context) , window, swapChain, swapchainImageViews, commandResources, defaultSyncResources);
+        return Core(std::move(context) , commandResources, defaultSyncResources);
     }
 
     const Context &Core::getContext() const
     {
         return m_Context;
     }
-    
-    const Swapchain& Core::getSwapchain() const {
-    	return m_swapchain;
-    }
 
-    Core::Core(Context &&context, Window &window, const Swapchain& swapChain,  std::vector<vk::ImageView> swapchainImageViews,
-        const CommandResources& commandResources, const SyncResources& syncResources) noexcept :
+    Core::Core(Context &&context, const CommandResources& commandResources, const SyncResources& syncResources) noexcept :
             m_Context(std::move(context)),
-			m_swapchain(swapChain),
-            m_window(window),
             m_PassManager{std::make_unique<PassManager>(m_Context.m_Device)},
             m_PipelineManager{std::make_unique<GraphicsPipelineManager>(m_Context.m_Device)},
             m_ComputePipelineManager{std::make_unique<ComputePipelineManager>(m_Context.m_Device)},
@@ -101,39 +59,23 @@ namespace vkcv
             m_SamplerManager(std::unique_ptr<SamplerManager>(new SamplerManager(m_Context.m_Device))),
             m_ImageManager{std::unique_ptr<ImageManager>(new ImageManager(*m_BufferManager))},
             m_CommandStreamManager{std::unique_ptr<CommandStreamManager>(new CommandStreamManager)},
+			m_WindowManager(std::make_unique<WindowManager>()),
+			m_SwapchainManager(std::make_unique<SwapchainManager>()),
             m_CommandResources(commandResources),
             m_SyncResources(syncResources)
 	{
 		m_BufferManager->m_core = this;
 		m_BufferManager->init();
 		m_CommandStreamManager->init(this);
-
+		m_SwapchainManager->m_context = &m_Context;
 		m_ImageManager->m_core = this;
-		
-		e_resizeHandle = m_window.e_resize.add( [&](int width, int height) {
-			m_swapchain.signalSwapchainRecreation();
-		});
-
-		const auto swapchainImages = m_Context.getDevice().getSwapchainImagesKHR(m_swapchain.getSwapchain());
-		m_ImageManager->setSwapchainImages(
-			swapchainImages, 
-			swapchainImageViews, 
-			swapChain.getExtent().width,
-			swapChain.getExtent().height,
-			swapChain.getFormat()
-		);
 	}
 
 	Core::~Core() noexcept {
-    	m_window.e_resize.remove(e_resizeHandle);
-    	
 		m_Context.getDevice().waitIdle();
 
 		destroyCommandResources(m_Context.getDevice(), m_CommandResources);
 		destroySyncResources(m_Context.getDevice(), m_SyncResources);
-
-		m_Context.m_Device.destroySwapchainKHR(m_swapchain.getSwapchain());
-		m_Context.m_Instance.destroySurfaceKHR(m_swapchain.getSurface());
 	}
 	
 	GraphicsPipelineHandle Core::createGraphicsPipeline(const GraphicsPipelineConfig &config)
@@ -151,13 +93,13 @@ namespace vkcv
         return m_PassManager->createPass(config);
     }
 
-	Result Core::acquireSwapchainImage() {
+	Result Core::acquireSwapchainImage(const SwapchainHandle &swapchainHandle) {
     	uint32_t imageIndex;
     	vk::Result result;
     	
 		try {
 			result = m_Context.getDevice().acquireNextImageKHR(
-					m_swapchain.getSwapchain(),
+					m_SwapchainManager->getSwapchain(swapchainHandle).getSwapchain(),
 					std::numeric_limits<uint64_t>::max(),
 					m_SyncResources.swapchainImageAcquired,
 					nullptr,
@@ -176,37 +118,29 @@ namespace vkcv
 		} else
 		if (result == vk::Result::eSuboptimalKHR) {
 			vkcv_log(LogLevel::WARNING, "Acquired image is suboptimal");
-			m_swapchain.signalSwapchainRecreation();
+			m_SwapchainManager->getSwapchain(swapchainHandle).signalSwapchainRecreation();
 		}
 		
 		m_currentSwapchainImageIndex = imageIndex;
 		return Result::SUCCESS;
 	}
 
-	bool Core::beginFrame(uint32_t& width, uint32_t& height) {
-		if (m_swapchain.shouldUpdateSwapchain()) {
+	bool Core::beginFrame(uint32_t& width, uint32_t& height, const WindowHandle &windowHandle) {
+		const SwapchainHandle swapchainHandle = m_WindowManager->getWindow(windowHandle).getSwapchainHandle();
+
+		if (m_SwapchainManager->getSwapchain(swapchainHandle).shouldUpdateSwapchain()) {
 			m_Context.getDevice().waitIdle();
 
-			m_swapchain.updateSwapchain(m_Context, m_window);
+			m_SwapchainManager->getSwapchain(swapchainHandle).updateSwapchain(m_Context, m_WindowManager->getWindow(windowHandle));
 			
-			if (!m_swapchain.getSwapchain()) {
+			if (!m_SwapchainManager->getSwapchain(swapchainHandle).getSwapchain()) {
 				return false;
 			}
-			
-			const auto swapchainImages = m_Context.getDevice().getSwapchainImagesKHR(m_swapchain.getSwapchain());
-			const auto swapchainViews = createSwapchainImageViews(m_Context, swapchainImages, m_swapchain.getFormat());
-			
-			const auto& extent = m_swapchain.getExtent();
 
-			m_ImageManager->setSwapchainImages(
-					swapchainImages,
-					swapchainViews,
-					extent.width, extent.height,
-					m_swapchain.getFormat()
-			);
+			setSwapchainImages(swapchainHandle);
 		}
 		
-		const auto& extent = m_swapchain.getExtent();
+		const auto& extent = m_SwapchainManager->getSwapchain(swapchainHandle).getExtent();
 		
 		width = extent.width;
 		height = extent.height;
@@ -215,7 +149,7 @@ namespace vkcv
 			return false;
 		}
 		
-    	if (acquireSwapchainImage() != Result::SUCCESS) {
+    	if (acquireSwapchainImage( swapchainHandle ) != Result::SUCCESS) {
 			vkcv_log(LogLevel::ERROR, "Acquire failed");
     		
     		m_currentSwapchainImageIndex = std::numeric_limits<uint32_t>::max();
@@ -334,13 +268,16 @@ namespace vkcv
 		const GraphicsPipelineHandle    &pipelineHandle,
         const PushConstants             &pushConstantData,
         const std::vector<DrawcallInfo> &drawcalls,
-		const std::vector<ImageHandle>  &renderTargets) {
+		const std::vector<ImageHandle>  &renderTargets,
+		const WindowHandle              &windowHandle) {
+
+		SwapchainHandle swapchainHandle = m_WindowManager->getWindow(windowHandle).getSwapchainHandle();
 
 		if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) {
 			return;
 		}
 
-		const std::array<uint32_t, 2> widthHeight = getWidthHeightFromRenderTargets(renderTargets, m_swapchain, *m_ImageManager);
+		const std::array<uint32_t, 2> widthHeight = getWidthHeightFromRenderTargets(renderTargets, m_SwapchainManager->getSwapchain(swapchainHandle), *m_ImageManager);
 		const auto width  = widthHeight[0];
 		const auto height = widthHeight[1];
 
@@ -354,7 +291,7 @@ namespace vkcv
 		vk::CommandBuffer cmdBuffer = m_CommandStreamManager->getStreamCommandBuffer(cmdStreamHandle);
 		transitionRendertargetsToAttachmentLayout(renderTargets, *m_ImageManager, cmdBuffer);
 
-		const vk::Framebuffer framebuffer = createFramebuffer(renderTargets, *m_ImageManager, m_swapchain, renderpass, m_Context.m_Device);
+		const vk::Framebuffer framebuffer = createFramebuffer(renderTargets, *m_ImageManager, m_SwapchainManager->getSwapchain(swapchainHandle), renderpass, m_Context.m_Device);
 
 		if (!framebuffer) {
 			vkcv_log(LogLevel::ERROR, "Failed to create temporary framebuffer");
@@ -399,13 +336,16 @@ namespace vkcv
 		const GraphicsPipelineHandle                        &pipelineHandle,
 		const PushConstants&                                pushConstantData,
 		const std::vector<MeshShaderDrawcall>&              drawcalls,
-		const std::vector<ImageHandle>&                     renderTargets) {
+		const std::vector<ImageHandle>&                     renderTargets,
+		const WindowHandle&                                 windowHandle) {
+
+		SwapchainHandle swapchainHandle = m_WindowManager->getWindow(windowHandle).getSwapchainHandle();
 
 		if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) {
 			return;
 		}
 
-		const std::array<uint32_t, 2> widthHeight = getWidthHeightFromRenderTargets(renderTargets, m_swapchain, *m_ImageManager);
+		const std::array<uint32_t, 2> widthHeight = getWidthHeightFromRenderTargets(renderTargets, m_SwapchainManager->getSwapchain(swapchainHandle), *m_ImageManager);
 		const auto width  = widthHeight[0];
 		const auto height = widthHeight[1];
 
@@ -419,7 +359,7 @@ namespace vkcv
 		vk::CommandBuffer cmdBuffer = m_CommandStreamManager->getStreamCommandBuffer(cmdStreamHandle);
 		transitionRendertargetsToAttachmentLayout(renderTargets, *m_ImageManager, cmdBuffer);
 
-		const vk::Framebuffer framebuffer = createFramebuffer(renderTargets, *m_ImageManager, m_swapchain, renderpass, m_Context.m_Device);
+		const vk::Framebuffer framebuffer = createFramebuffer(renderTargets, *m_ImageManager, m_SwapchainManager->getSwapchain(swapchainHandle), renderpass, m_Context.m_Device);
 
 		if (!framebuffer) {
 			vkcv_log(LogLevel::ERROR, "Failed to create temporary framebuffer");
@@ -579,20 +519,20 @@ namespace vkcv
 		recordCommandsToStream(cmdStream, submitFunction, nullptr);
 	}
 
-	void Core::endFrame() {
+	void Core::endFrame(const WindowHandle& windowHandle) {
+
+		SwapchainHandle swapchainHandle = m_WindowManager->getWindow(windowHandle).getSwapchainHandle();
+
 		if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) {
 			return;
 		}
-  
-		const auto swapchainImages = m_Context.getDevice().getSwapchainImagesKHR(m_swapchain.getSwapchain());
-
-		const auto& queueManager = m_Context.getQueueManager();
+		
 		std::array<vk::Semaphore, 2> waitSemaphores{
 			m_SyncResources.renderFinished,
 			m_SyncResources.swapchainImageAcquired
 		};
 
-		const vk::SwapchainKHR& swapchain = m_swapchain.getSwapchain();
+		const vk::SwapchainKHR& swapchain = m_SwapchainManager->getSwapchain(swapchainHandle).getSwapchain();
 		const vk::PresentInfoKHR presentInfo(
 			waitSemaphores,
 			swapchain,
@@ -602,7 +542,7 @@ namespace vkcv
 		vk::Result result;
 		
 		try {
-			result = queueManager.getPresentQueue().handle.presentKHR(presentInfo);
+			result = m_Context.getDevice().getQueue(m_SwapchainManager->getSwapchain(swapchainHandle).getPresentQueueIndex(),0).presentKHR(presentInfo);
 		} catch (const vk::OutOfDateKHRError& e) {
 			result = vk::Result::eErrorOutOfDateKHR;
 		} catch (const vk::DeviceLostError& e) {
@@ -615,7 +555,7 @@ namespace vkcv
 		} else
 		if (result == vk::Result::eSuboptimalKHR) {
 			vkcv_log(LogLevel::WARNING, "Swapchain presentation is suboptimal");
-			m_swapchain.signalSwapchainRecreation();
+			m_SwapchainManager->signalRecreation(swapchainHandle);
 		}
 	}
 	
@@ -707,6 +647,22 @@ namespace vkcv
 			multisampling);
 	}
 
+	WindowHandle Core::createWindow(
+			const char *applicationName,
+			uint32_t windowWidth,
+			uint32_t windowHeight,
+			bool resizeable) {
+
+		WindowHandle windowHandle = m_WindowManager->createWindow(*m_SwapchainManager ,applicationName, windowWidth, windowHeight, resizeable);
+		SwapchainHandle swapchainHandle = m_WindowManager->getWindow(windowHandle).getSwapchainHandle();
+		setSwapchainImages( swapchainHandle );
+		return windowHandle;
+	}
+
+	Window& Core::getWindow(const WindowHandle& handle) {
+		return m_WindowManager->getWindow(handle);
+	}
+
 	uint32_t Core::getImageWidth(const ImageHandle& image)
 	{
 		return m_ImageManager->getImageWidth(image);
@@ -721,6 +677,19 @@ namespace vkcv
 		return m_ImageManager->getImageFormat(image);
 	}
 
+	Swapchain& Core::getSwapchainOfCurrentWindow() {
+		return m_SwapchainManager->getSwapchain(Window::getFocusedWindow().getSwapchainHandle());
+	}
+
+	Swapchain& Core::getSwapchain(const SwapchainHandle& handle) {
+		return m_SwapchainManager->getSwapchain(handle);
+	}
+
+	Swapchain& Core::getSwapchain(const WindowHandle& handle) {
+		SwapchainHandle swapchainHandle = m_WindowManager->getWindow(handle).getSwapchainHandle();
+		return getSwapchain(swapchainHandle);
+	}
+
 	DescriptorSetLayoutHandle Core::createDescriptorSetLayout(const DescriptorBindings &bindingsMap)
 	{
 	    return m_DescriptorManager->createDescriptorSetLayout(bindingsMap);
@@ -870,6 +839,20 @@ namespace vkcv
 			);
 		}, nullptr);
 	}
+
+	void Core::setSwapchainImages( SwapchainHandle handle ) {
+		Swapchain swapchain = m_SwapchainManager->getSwapchain(handle);
+		const auto swapchainImages = m_SwapchainManager->getSwapchainImages(handle);
+		const auto swapchainImageViews = m_SwapchainManager->createSwapchainImageViews(handle);
+
+		m_ImageManager->setSwapchainImages(
+				swapchainImages,
+				swapchainImageViews,
+				swapchain.getExtent().width,
+				swapchain.getExtent().height,
+				swapchain.getFormat()
+		);
+	}
 	
 	static void setDebugObjectLabel(const vk::Device& device, const vk::ObjectType& type,
 									uint64_t handle, const std::string& label) {
@@ -1023,5 +1006,4 @@ namespace vkcv
 				label
 		);
 	}
-	
 }
diff --git a/src/vkcv/QueueManager.cpp b/src/vkcv/QueueManager.cpp
index 4b3af103e6b78a0a05f12ee9c743d9ca63450d44..ff134041bdc6279d312df668cab448a50750f1c3 100644
--- a/src/vkcv/QueueManager.cpp
+++ b/src/vkcv/QueueManager.cpp
@@ -5,6 +5,7 @@
 
 #include "vkcv/QueueManager.hpp"
 #include "vkcv/Logger.hpp"
+#include "vkcv/Swapchain.hpp"
 
 namespace vkcv {
 
@@ -85,7 +86,7 @@ namespace vkcv {
                     found = false;
                     for (size_t i = 0; i < queueFamilyStatus.size() && !found; i++) {
                         if (queueFamilyStatus[i][0] > 0) {
-                            queuePairsGraphics.push_back(std::pair(i, initialQueueFamilyStatus[i][0] - queueFamilyStatus[i][0]));
+                            queuePairsGraphics.emplace_back(std::pair(i, initialQueueFamilyStatus[i][0] - queueFamilyStatus[i][0]));
                             queueFamilyStatus[i][0]--;
                             queueFamilyStatus[i][1]--;
                             queueFamilyStatus[i][2]--;
@@ -95,7 +96,7 @@ namespace vkcv {
                     if (!found) {
                         for (size_t i = 0; i < queueFamilyStatus.size() && !found; i++) {
                             if (initialQueueFamilyStatus[i][0] > 0) {
-                                queuePairsGraphics.push_back(std::pair(i, 0));
+                                queuePairsGraphics.emplace_back(std::pair(i, 0));
                                 found = true;
                             }
                         }
@@ -107,7 +108,7 @@ namespace vkcv {
                     found = false;
                     for (size_t i = 0; i < queueFamilyStatus.size() && !found; i++) {
                         if (queueFamilyStatus[i][1] > 0) {
-                            queuePairsCompute.push_back(std::pair(i, initialQueueFamilyStatus[i][1] - queueFamilyStatus[i][1]));
+                            queuePairsCompute.emplace_back(std::pair(i, initialQueueFamilyStatus[i][1] - queueFamilyStatus[i][1]));
                             queueFamilyStatus[i][0]--;
                             queueFamilyStatus[i][1]--;
                             queueFamilyStatus[i][2]--;
@@ -117,7 +118,7 @@ namespace vkcv {
                     if (!found) {
                         for (size_t i = 0; i < queueFamilyStatus.size() && !found; i++) {
                             if (initialQueueFamilyStatus[i][1] > 0) {
-                                queuePairsCompute.push_back(std::pair(i, 0));
+                                queuePairsCompute.emplace_back(std::pair(i, 0));
                                 found = true;
                             }
                         }
@@ -129,7 +130,7 @@ namespace vkcv {
                     found = false;
                     for (size_t i = 0; i < queueFamilyStatus.size() && !found; i++) {
                         if (queueFamilyStatus[i][2] > 0) {
-                            queuePairsTransfer.push_back(std::pair(i, initialQueueFamilyStatus[i][2] - queueFamilyStatus[i][2]));
+                            queuePairsTransfer.emplace_back(std::pair(i, initialQueueFamilyStatus[i][2] - queueFamilyStatus[i][2]));
                             queueFamilyStatus[i][0]--;
                             queueFamilyStatus[i][1]--;
                             queueFamilyStatus[i][2]--;
@@ -139,7 +140,7 @@ namespace vkcv {
                     if (!found) {
                         for (size_t i = 0; i < queueFamilyStatus.size() && !found; i++) {
                             if (initialQueueFamilyStatus[i][2] > 0) {
-                                queuePairsTransfer.push_back(std::pair(i, 0));
+                                queuePairsTransfer.emplace_back(std::pair(i, 0));
                                 found = true;
                             }
                         }
@@ -194,13 +195,33 @@ namespace vkcv {
                                       std::vector<std::pair<int, int>> &queuePairsTransfer) {
 
         std::vector<Queue> graphicsQueues = getQueues(device, queuePairsGraphics);
-        std::vector<Queue> computeQueues = getQueues(device, queuePairsCompute );
+        std::vector<Queue> computeQueues  = getQueues(device, queuePairsCompute);
         std::vector<Queue> transferQueues = getQueues(device, queuePairsTransfer);
 
     	return QueueManager( std::move(graphicsQueues), std::move(computeQueues), std::move(transferQueues), 0);
 	}
 
-	QueueManager::QueueManager(std::vector<Queue>&& graphicsQueues, std::vector<Queue>&& computeQueues, std::vector<Queue>&& transferQueues, size_t presentIndex)
+	uint32_t QueueManager::checkSurfaceSupport(
+			const vk::PhysicalDevice &physicalDevice,
+			vk::SurfaceKHR &surface
+	) {
+		std::vector<vk::QueueFamilyProperties> qFamilyProperties = physicalDevice.getQueueFamilyProperties();
+
+		for(uint32_t i = 0; i < qFamilyProperties.size(); i++) {
+			vk::Bool32 presentSupport;
+			
+			if ((vk::Result::eSuccess == physicalDevice.getSurfaceSupportKHR(i, surface, &presentSupport)) &&
+				(presentSupport == VK_TRUE)) {
+				return i;
+			}
+		}
+		
+		vkcv_log(LogLevel::WARNING, "No supported present queue");
+		return 0;
+	}
+
+	QueueManager::QueueManager(std::vector<Queue>&& graphicsQueues, std::vector<Queue>&& computeQueues,
+							   std::vector<Queue>&& transferQueues, size_t presentIndex)
 	: m_graphicsQueues(graphicsQueues), m_computeQueues(computeQueues), m_transferQueues(transferQueues), m_presentIndex(presentIndex)
     {}
 
diff --git a/src/vkcv/Swapchain.cpp b/src/vkcv/Swapchain.cpp
index 3174807c7fd6073be58d2dbaf3c1ceb2639d1add..483732927bc2cc507f0868226b65bf4d49e27048 100644
--- a/src/vkcv/Swapchain.cpp
+++ b/src/vkcv/Swapchain.cpp
@@ -163,10 +163,11 @@ namespace vkcv
         const vk::Device& device = context.getDevice();
 
         Surface surface;
-        surface.handle       = createSurface(window.getWindow(), instance, physicalDevice);
-        surface.formats      = physicalDevice.getSurfaceFormatsKHR(surface.handle);
-        surface.capabilities = physicalDevice.getSurfaceCapabilitiesKHR(surface.handle);
-        surface.presentModes = physicalDevice.getSurfacePresentModesKHR(surface.handle);
+        surface.handle            = createSurface(window.getWindow(), instance, physicalDevice);
+        surface.formats           = physicalDevice.getSurfaceFormatsKHR(surface.handle);
+        surface.capabilities      = physicalDevice.getSurfaceCapabilitiesKHR(surface.handle);
+        surface.presentModes      = physicalDevice.getSurfacePresentModesKHR(surface.handle);
+		surface.presentQueueIndex = QueueManager::checkSurfaceSupport(physicalDevice, surface.handle);
 
         vk::Extent2D chosenExtent = chooseExtent(physicalDevice, surface.handle, window);
         vk::SurfaceFormatKHR chosenSurfaceFormat = chooseSurfaceFormat(physicalDevice, surface.handle);
@@ -264,4 +265,8 @@ namespace vkcv
 	uint32_t Swapchain::getImageCount() const {
 		return m_ImageCount;
 	}
+
+	const uint32_t &Swapchain::getPresentQueueIndex() const {
+		return m_Surface.presentQueueIndex;
+	}
 }
diff --git a/src/vkcv/SwapchainManager.cpp b/src/vkcv/SwapchainManager.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0caea9fd0fe47b4f51a53adac9134fb7f81a205a
--- /dev/null
+++ b/src/vkcv/SwapchainManager.cpp
@@ -0,0 +1,85 @@
+#include "SwapchainManager.hpp"
+
+namespace vkcv {
+
+	SwapchainManager::SwapchainManager() noexcept {
+	}
+
+	SwapchainManager::~SwapchainManager() noexcept {
+		for (uint64_t id = 0; id < m_swapchains.size(); id++) {
+			destroySwapchainById(id);
+		}
+		m_swapchains.clear();
+	}
+
+	SwapchainHandle SwapchainManager::createSwapchain(Window &window) {
+		const uint64_t id = m_swapchains.size();
+
+		Swapchain swapchain = Swapchain::create(window, *m_context);
+
+		m_swapchains.push_back(swapchain);
+		SwapchainHandle swapchainHandle = SwapchainHandle(id, [&](uint64_t id) { destroySwapchainById(id); });
+		window.m_swapchainHandle = swapchainHandle;
+		return swapchainHandle;
+	}
+
+	Swapchain& SwapchainManager::getSwapchain(const SwapchainHandle& handle) {
+		return m_swapchains[handle.getId()];
+	}
+
+	void SwapchainManager::destroySwapchainById(uint64_t id) {
+
+		if (id >= m_swapchains.size()) {
+			vkcv_log(LogLevel::ERROR, "Invalid id");
+			return;
+		}
+		Swapchain &swapchain = m_swapchains[id];
+
+		if (swapchain.m_Swapchain) {
+			m_context->getDevice().destroySwapchainKHR(swapchain.m_Swapchain);
+			swapchain.m_Swapchain = nullptr;
+		}
+		
+		if (swapchain.m_Surface.handle) {
+			m_context->getInstance().destroySurfaceKHR(swapchain.m_Surface.handle);
+			swapchain.m_Surface.handle = nullptr;
+		}
+	}
+
+	void SwapchainManager::signalRecreation(const SwapchainHandle& handle) {
+		m_swapchains[handle.getId()].signalSwapchainRecreation();
+	}
+
+	std::vector<vk::Image> SwapchainManager::getSwapchainImages(const SwapchainHandle& handle) {
+		return m_context->getDevice().getSwapchainImagesKHR(m_swapchains[handle.getId()].getSwapchain());
+	}
+
+	std::vector<vk::ImageView> SwapchainManager::createSwapchainImageViews(SwapchainHandle& handle) {
+		std::vector<vk::Image> images = getSwapchainImages(handle);
+		Swapchain &swapchain = m_swapchains[handle.getId()];
+
+		std::vector<vk::ImageView> imageViews;
+		imageViews.reserve(images.size());
+		//here can be swizzled with vk::ComponentSwizzle if needed
+		vk::ComponentMapping componentMapping(
+				vk::ComponentSwizzle::eR,
+				vk::ComponentSwizzle::eG,
+				vk::ComponentSwizzle::eB,
+				vk::ComponentSwizzle::eA);
+
+		vk::ImageSubresourceRange subResourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1);
+
+		for (auto image : images) {
+			vk::ImageViewCreateInfo imageViewCreateInfo(
+					vk::ImageViewCreateFlags(),
+					image,
+					vk::ImageViewType::e2D,
+					swapchain.getFormat(),
+					componentMapping,
+					subResourceRange);
+
+			imageViews.push_back(m_context->getDevice().createImageView(imageViewCreateInfo));
+		}
+		return imageViews;
+	}
+}
\ No newline at end of file
diff --git a/src/vkcv/SwapchainManager.hpp b/src/vkcv/SwapchainManager.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..58478ef09af1734bef7df217550d43af74fd8424
--- /dev/null
+++ b/src/vkcv/SwapchainManager.hpp
@@ -0,0 +1,72 @@
+#pragma once
+
+#include <vector>
+#include <GLFW/glfw3.h>
+
+#include "WindowManager.hpp"
+#include "vkcv/Swapchain.hpp"
+#include "vkcv/Handles.hpp"
+
+namespace vkcv {
+	class Core;
+
+	class SwapchainManager {
+		friend class Core;
+
+		friend class WindowManager;
+
+	private:
+		std::vector<Swapchain> m_swapchains;
+
+		Context *m_context;
+
+		void destroySwapchainById(uint64_t id);
+
+	public:
+		SwapchainManager() noexcept;
+
+		~SwapchainManager() noexcept;
+
+		SwapchainManager(SwapchainManager &&other) = delete;
+
+		SwapchainManager(const SwapchainManager &other) = delete;
+
+		SwapchainManager &operator=(SwapchainManager &&other) = delete;
+
+		SwapchainManager &operator=(const SwapchainManager &other) = delete;
+
+		/**
+		 * creates a swapchain and returns the handle
+		 * @param window of the to  creatable window
+		 * @return the swapchainHandle of the created swapchain
+		 */
+		SwapchainHandle createSwapchain(Window &window);
+
+		/**
+		 * @param handle of the swapchain to get
+		 * @return the reference of the swapchain
+		 */
+		[[nodiscard]]
+		Swapchain &getSwapchain(const SwapchainHandle& handle);
+
+		/**
+		 * sets  the recreation  flag fot the swapchain
+		 * @param handle of the swapchain that should be recreated
+		 */
+		void signalRecreation(const SwapchainHandle& handle);
+
+		/**
+		 * gets the swapchain images
+		 * @param handle of the swapchain
+		 * @return a vector of the swapchain images
+		 */
+		std::vector<vk::Image> getSwapchainImages(const SwapchainHandle& handle);
+
+		/**
+		 * creates the swapchain imageViews for the swapchain
+		 * @param handle of the swapchain which ImageViews should be created
+		 * @return a ov ImageViews of the swapchain
+		 */
+		std::vector<vk::ImageView> createSwapchainImageViews(SwapchainHandle& handle);
+	};
+}
\ No newline at end of file
diff --git a/src/vkcv/Window.cpp b/src/vkcv/Window.cpp
index 072efcd00eb6520fa4f20379721b559668339f6e..2b49ea32b21dc5d2497f6844c17baf8c3454640e 100644
--- a/src/vkcv/Window.cpp
+++ b/src/vkcv/Window.cpp
@@ -1,8 +1,3 @@
-/**
- * @authors Sebastian Gaida
- * @file src/vkcv/Window.cpp
- * @brief Window class to handle a basic rendering surface and input
- */
 
 #include <thread>
 #include <vector>
@@ -11,11 +6,117 @@
 #include "vkcv/Window.hpp"
 
 namespace vkcv {
+	
+	void Window_onMouseButtonEvent(GLFWwindow *callbackWindow, int button, int action, int mods) {
+		auto window = static_cast<Window *>(glfwGetWindowUserPointer(callbackWindow));
+
+		if (window != nullptr) {
+			window->e_mouseButton(button, action, mods);
+		}
+	}
+
+	void Window_onMouseMoveEvent(GLFWwindow *callbackWindow, double x, double y) {
+		auto window = static_cast<Window *>(glfwGetWindowUserPointer(callbackWindow));
+
+		if (window != nullptr) {
+			window->e_mouseMove(x, y);
+		}
+	}
+
+	void Window_onMouseScrollEvent(GLFWwindow *callbackWindow, double xoffset, double yoffset) {
+		auto window = static_cast<Window *>(glfwGetWindowUserPointer(callbackWindow));
+
+		if (window != nullptr) {
+			window->e_mouseScroll(xoffset, yoffset);
+		}
+	}
+
+	void Window_onResize(GLFWwindow *callbackWindow, int width, int height) {
+		auto window = static_cast<Window *>(glfwGetWindowUserPointer(callbackWindow));
 
+		if (window != nullptr) {
+			window->e_resize(width, height);
+		}
+	}
+
+	void Window_onKeyEvent(GLFWwindow *callbackWindow, int key, int scancode, int action, int mods) {
+		auto window = static_cast<Window *>(glfwGetWindowUserPointer(callbackWindow));
+
+		if (window != nullptr) {
+			window->e_key(key, scancode, action, mods);
+		}
+	}
+ 
+	void Window_onCharEvent(GLFWwindow *callbackWindow, unsigned int c) {
+		auto window = static_cast<Window *>(glfwGetWindowUserPointer(callbackWindow));
+	
+		if (window != nullptr) {
+			window->e_char(c);
+		}
+	}
+	
 	static std::vector<GLFWwindow*> s_Windows;
 
-    Window::Window(GLFWwindow *window) :
-    m_window(window),
+	void Window_onGamepadEvent(int gamepadIndex) {
+			Window::getFocusedWindow().e_gamepad(gamepadIndex);
+	}
+	
+	static GLFWwindow* createGLFWWindow(const char *windowTitle, int width, int height, bool resizable) {
+		if(s_Windows.empty()) {
+			glfwInit();
+		}
+	
+		width = std::max(width, 1);
+		height = std::max(height, 1);
+	
+		glfwDefaultWindowHints();
+		glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
+		glfwWindowHint(GLFW_RESIZABLE, resizable ? GLFW_TRUE : GLFW_FALSE);
+  
+		GLFWwindow *window = glfwCreateWindow(width, height, windowTitle, nullptr, nullptr);
+	
+		if (window) {
+			s_Windows.push_back(window);
+		}
+  
+		return window;
+	}
+	
+	static void bindGLFWWindow(GLFWwindow *windowHandle, Window* window) {
+		if (!windowHandle) {
+			return;
+		}
+		
+		glfwSetWindowUserPointer(windowHandle, window);
+	
+		// combine Callbacks with Events
+		glfwSetMouseButtonCallback(windowHandle, Window_onMouseButtonEvent);
+		glfwSetCursorPosCallback(windowHandle, Window_onMouseMoveEvent);
+		glfwSetWindowSizeCallback(windowHandle, Window_onResize);
+		glfwSetKeyCallback(windowHandle, Window_onKeyEvent);
+		glfwSetScrollCallback(windowHandle, Window_onMouseScrollEvent);
+		glfwSetCharCallback(windowHandle, Window_onCharEvent);
+	}
+	
+	Window::Window() :
+	m_title(),
+	m_resizable(false),
+	m_shouldClose(false),
+	m_window(nullptr),
+	e_mouseButton(true),
+	e_mouseMove(true),
+	e_mouseScroll(true),
+	e_resize(true),
+	e_key(true),
+	e_char(true),
+	e_gamepad(true)
+	{}
+
+	Window::Window(const char* title, int width, int height, bool resizable) :
+    m_title(title),
+    m_resizable(resizable),
+	m_shouldClose(false),
+    m_window(createGLFWWindow(title, width, height, resizable)),
 	e_mouseButton(true),
 	e_mouseMove(true),
 	e_mouseScroll(true),
@@ -24,15 +125,7 @@ namespace vkcv {
 	e_char(true),
 	e_gamepad(true)
     {
-		glfwSetWindowUserPointer(m_window, this);
-	
-		// combine Callbacks with Events
-		glfwSetMouseButtonCallback(m_window, Window::onMouseButtonEvent);
-		glfwSetCursorPosCallback(m_window, Window::onMouseMoveEvent);
-		glfwSetWindowSizeCallback(m_window, Window::onResize);
-		glfwSetKeyCallback(m_window, Window::onKeyEvent);
-		glfwSetScrollCallback(m_window, Window::onMouseScrollEvent);
-		glfwSetCharCallback(m_window, Window::onCharEvent);
+		bindGLFWWindow(m_window, this);
     }
 
     Window::~Window() {
@@ -43,31 +136,28 @@ namespace vkcv {
         Window::e_key.unlock();
         Window::e_char.unlock();
         Window::e_gamepad.unlock();
-
-		s_Windows.erase(std::find(s_Windows.begin(), s_Windows.end(), m_window));
-        glfwDestroyWindow(m_window);
+		Window::e_resize.remove(m_resizeHandle);
+        if (m_window) {
+        	s_Windows.erase(std::find(s_Windows.begin(), s_Windows.end(), m_window));
+        	glfwDestroyWindow(m_window);
+        }
 
         if(s_Windows.empty()) {
             glfwTerminate();
         }
     }
 
-    Window Window::create( const char *windowTitle, int width, int height, bool resizable) {
-		if(s_Windows.empty()) {
-			glfwInit();
+	bool Window::hasOpenWindow() {
+		for (auto glfwWindow : s_Windows) {
+			auto window = static_cast<Window *>(glfwGetWindowUserPointer(glfwWindow));
+			
+			if (window->isOpen()) {
+				return true;
+			}
 		}
-	
-		width = std::max(width, 1);
-		height = std::max(height, 1);
-	
-		glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
-		glfwWindowHint(GLFW_RESIZABLE, resizable ? GLFW_TRUE : GLFW_FALSE);
-		GLFWwindow *window = glfwCreateWindow(width, height, windowTitle, nullptr, nullptr);
-	
-		s_Windows.push_back(window);
-	
-		return Window(window);
-    }
+		
+		return false;
+	}
 
     void Window::pollEvents() {
 
@@ -92,7 +182,7 @@ namespace vkcv {
 
 		for (int gamepadIndex = GLFW_JOYSTICK_1; gamepadIndex <= GLFW_JOYSTICK_LAST; gamepadIndex++) {
 			if (glfwJoystickPresent(gamepadIndex)) {
-				onGamepadEvent(gamepadIndex);
+				Window_onGamepadEvent(gamepadIndex);
 			}
 		}
 	
@@ -106,96 +196,101 @@ namespace vkcv {
 			window->e_key.lock();
 			window->e_char.lock();
 			window->e_gamepad.lock();
+			
+			window->m_shouldClose |= glfwWindowShouldClose(glfwWindow);
 		}
     }
+	
+	const std::vector<std::string>& Window::getExtensions() {
+		static std::vector<std::string> extensions;
+		
+		if (extensions.empty()) {
+			if(s_Windows.empty()) {
+				glfwInit();
+			}
+			
+			uint32_t glfwExtensionCount = 0;
+			const char** glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
+			
+			for (uint32_t i = 0; i < glfwExtensionCount; i++) {
+				extensions.emplace_back(glfwExtensions[i]);
+			}
+			
+			if (s_Windows.empty()) {
+				glfwTerminate();
+			}
+		}
+		
+		return extensions;
+	}
 
-    void Window::onMouseButtonEvent(GLFWwindow *callbackWindow, int button, int action, int mods) {
-        auto window = static_cast<Window *>(glfwGetWindowUserPointer(callbackWindow));
-
-        if (window != nullptr) {
-            window->e_mouseButton(button, action, mods);
-        }
+    bool Window::isOpen() const {
+		if (!m_window) {
+			return false;
+		}
+		
+        return !m_shouldClose;
     }
-
-    void Window::onMouseMoveEvent(GLFWwindow *callbackWindow, double x, double y) {
-        auto window = static_cast<Window *>(glfwGetWindowUserPointer(callbackWindow));
-
-        if (window != nullptr) {
-            window->e_mouseMove(x, y);
-        }
+    
+    const std::string& Window::getTitle() const {
+    	return m_title;
     }
 
-    void Window::onMouseScrollEvent(GLFWwindow *callbackWindow, double xoffset, double yoffset) {
-        auto window = static_cast<Window *>(glfwGetWindowUserPointer(callbackWindow));
-
-        if (window != nullptr) {
-            window->e_mouseScroll(xoffset, yoffset);
+    int Window::getWidth() const {
+        int width = 0;
+        
+        if (m_window) {
+        	glfwGetWindowSize(m_window, &width, nullptr);
         }
+        
+        return std::max(width, 1);
     }
 
-    void Window::onResize(GLFWwindow *callbackWindow, int width, int height) {
-        auto window = static_cast<Window *>(glfwGetWindowUserPointer(callbackWindow));
-
-        if (window != nullptr) {
-            window->e_resize(width, height);
+    int Window::getHeight() const {
+        int height = 0;
+        
+        if (m_window) {
+        	glfwGetWindowSize(m_window, nullptr, &height);
         }
+        
+        return std::max(height, 1);
     }
+	
+	bool Window::isResizable() const {
+		return m_resizable;
+	}
 
-    void Window::onKeyEvent(GLFWwindow *callbackWindow, int key, int scancode, int action, int mods) {
-        auto window = static_cast<Window *>(glfwGetWindowUserPointer(callbackWindow));
-
-        if (window != nullptr) {
-            window->e_key(key, scancode, action, mods);
-        }
+    GLFWwindow* Window::getWindow() const {
+        return m_window;
     }
     
-    void Window::onCharEvent(GLFWwindow *callbackWindow, unsigned int c) {
-		auto window = static_cast<Window *>(glfwGetWindowUserPointer(callbackWindow));
-	
-		if (window != nullptr) {
-			window->e_char(c);
+    void Window::getFramebufferSize(int &width, int &height) const {
+		if (m_window) {
+			glfwGetFramebufferSize(m_window, &width, &height);
+		} else {
+			width = 0;
+			height = 0;
 		}
     }
 
-    void Window::onGamepadEvent(int gamepadIndex) {
-        size_t activeWindowIndex = std::find_if(
-        		s_Windows.begin(),
-        		s_Windows.end(),
-        		[](GLFWwindow* window){return glfwGetWindowAttrib(window, GLFW_FOCUSED);}
-		) - s_Windows.begin();
-	
-		// fixes index getting out of bounds (e.g. if there is no focused window)
-        activeWindowIndex *= (activeWindowIndex < s_Windows.size());
-        
-        auto window = static_cast<Window *>(glfwGetWindowUserPointer(s_Windows[activeWindowIndex]));
+	Window& Window::getFocusedWindow() {
+		static Window defaultWindow;
 
-        if (window != nullptr) {
-            window->e_gamepad(gamepadIndex);
-        }
-    }
-
-    bool Window::isWindowOpen() const {
-        return !glfwWindowShouldClose(m_window);
-    }
+		auto activeWindowIterator = std::find_if(
+				s_Windows.begin(),
+				s_Windows.end(),
+				[](GLFWwindow *window) { return glfwGetWindowAttrib(window, GLFW_FOCUSED); }
+		);
 
-    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;
-    }
+		if( activeWindowIterator == s_Windows.end() )
+		{
+			return defaultWindow;
+		}
+		Window& window = *static_cast<Window *>(glfwGetWindowUserPointer(*activeWindowIterator));
+		return window;
+	}
 
-    GLFWwindow *Window::getWindow() const {
-        return m_window;
-    }
-    
-    void Window::getFramebufferSize(int &width, int &height) const {
-		glfwGetFramebufferSize(m_window, &width, &height);
-    }
-    
+	SwapchainHandle Window::getSwapchainHandle() const {
+		return m_swapchainHandle;
+	}
 }
diff --git a/src/vkcv/WindowManager.cpp b/src/vkcv/WindowManager.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3179b219e9e93905d7202cf8e11dbbf0e019020f
--- /dev/null
+++ b/src/vkcv/WindowManager.cpp
@@ -0,0 +1,56 @@
+#include "WindowManager.hpp"
+
+namespace vkcv {
+	
+	WindowManager::WindowManager() noexcept {
+	}
+
+	WindowManager::~WindowManager() noexcept {
+		for (uint64_t id = 0; id < m_windows.size(); id++) {
+			destroyWindowById(id);
+		}
+		
+		m_windows.clear();
+	}
+
+	WindowHandle WindowManager::createWindow(
+			SwapchainManager &swapchainManager,
+			const char *applicationName,
+			uint32_t windowWidth,
+			uint32_t windowHeight,
+			bool resizeable) {
+		const uint64_t id = m_windows.size();
+
+		auto window = new Window(applicationName, windowWidth, windowHeight, resizeable);
+
+		SwapchainHandle swapchainHandle = swapchainManager.createSwapchain(*window);
+
+		if (resizeable) {
+			const event_handle<int, int> &resizeHandle = window->e_resize.add([&,handle=swapchainHandle](int width, int height) {
+				// copy handle because it would run out of scope and be invalid
+				swapchainManager.signalRecreation(handle);
+			});
+			window->m_resizeHandle = resizeHandle;
+		}
+
+		m_windows.push_back(window);
+		return WindowHandle(id, [&](uint64_t id) { destroyWindowById(id); });
+	}
+
+	Window &WindowManager::getWindow(const WindowHandle handle) const {
+		return *m_windows[handle.getId()];
+	}
+
+	void WindowManager::destroyWindowById(uint64_t id) {
+
+		if (id >= m_windows.size()) {
+			vkcv_log(LogLevel::ERROR, "Invalid id");
+			return;
+		}
+
+		if (m_windows[id] != nullptr) {
+			delete m_windows[id];
+			m_windows[id] = nullptr;
+		}
+	}
+}
\ No newline at end of file
diff --git a/src/vkcv/WindowManager.hpp b/src/vkcv/WindowManager.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..66186c494c38a5af46ffe34cc31b2e86bbbc95db
--- /dev/null
+++ b/src/vkcv/WindowManager.hpp
@@ -0,0 +1,58 @@
+#pragma once
+
+#include <memory>
+#include <vector>
+#include <GLFW/glfw3.h>
+
+#include "vkcv/Window.hpp"
+#include "vkcv/Handles.hpp"
+#include "SwapchainManager.hpp"
+
+namespace vkcv {
+	class Context;
+
+	class SwapchainManager;
+
+	class WindowManager {
+		friend class Core;
+
+	private:
+		std::vector<Window*> m_windows;
+
+		void destroyWindowById(uint64_t id);
+
+	public:
+		WindowManager() noexcept;
+
+		~WindowManager() noexcept;
+
+		WindowManager(WindowManager &&other) = delete;
+
+		WindowManager(const WindowManager &other) = delete;
+
+		WindowManager &operator=(WindowManager &&other) = delete;
+
+		WindowManager &operator=(const WindowManager &other) = delete;
+
+		/**
+		 * creates a window and returns it's  handle
+		 * @param swapchainManager for swapchain creation
+		 * @param applicationName name of the window
+		 * @param windowWidth
+		 * @param windowHeight
+		 * @param resizeable if the window is resizable
+		 * @return window handle
+		 */
+		WindowHandle createWindow(SwapchainManager &swapchainManager, const char *applicationName, uint32_t windowWidth,
+								  uint32_t windowHeight,
+								  bool resizeable);
+
+		/**
+		 * @param handle of the window to get
+		 * @return the reference of the window
+		 */
+		[[nodiscard]]
+		Window &getWindow(const WindowHandle handle) const;
+
+	};
+}
\ No newline at end of file