diff --git a/README.md b/README.md
index 4289a74308776017f099d048cf749c9693e6609f..7872f23fca996d5f339964a0932e83601cbd3c35 100644
--- a/README.md
+++ b/README.md
@@ -13,3 +13,13 @@ More information about Git LFS [here](https://git-lfs.github.com/).
 
 Git submodules are used for libraries. 
 To download the submodules either clone using `git clone --recurse-submodules` or after `git clone` use `git submodule init` and `git submodule update`.
+
+## Documentation
+
+The documentation for the develop-branch can be found here:  
+https://vkcv.de/develop/  
+
+The documentation concerning the respective merge request is listed here:  
+https://vkcv.de/branch/  
+
+It is automatically generated and uploaded using the CI pipeline.
\ No newline at end of file
diff --git a/include/vkcv/Buffer.hpp b/include/vkcv/Buffer.hpp
index 7cace9324e6b1d885974c1c8906db63d5d8b095d..5a95471bf2ccfb0709ee31e1e6131817575ee0ff 100644
--- a/include/vkcv/Buffer.hpp
+++ b/include/vkcv/Buffer.hpp
@@ -4,63 +4,70 @@
  * @file vkcv/Buffer.hpp
  * @brief template buffer class, template for type security, implemented here because template classes can't be written in .cpp
  */
+#include "Handles.hpp"
 #include "BufferManager.hpp"
 
 namespace vkcv {
 
 	template<typename T>
 	class Buffer {
-		friend Core;
-
+		friend class Core;
 	public:
 		// explicit destruction of default constructor
 		Buffer<T>() = delete;
-
-		BufferType getType() {
+		
+		[[nodiscard]]
+		const BufferHandle& getHandle() const {
+			return m_handle;
+		}
+		
+		[[nodiscard]]
+		BufferType getType() const {
 			return m_type;
 		};
 		
-		size_t getCount() {
+		[[nodiscard]]
+		size_t getCount() const {
 			return m_count;
 		}
 		
-		size_t getSize() {
+		[[nodiscard]]
+		size_t getSize() const {
 			return m_count * sizeof(T);
 		}
 		
 		void fill(T* data, size_t count = 0, size_t offset = 0) {
-			 m_manager->fillBuffer(m_handle_id, data, count * sizeof(T), offset * sizeof(T));
+			 m_manager->fillBuffer(m_handle, data, count * sizeof(T), offset * sizeof(T));
 		}
 		
+		[[nodiscard]]
 		T* map(size_t offset = 0, size_t count = 0) {
-			return reinterpret_cast<T*>(m_manager->mapBuffer(m_handle_id, offset * sizeof(T), count * sizeof(T)));
+			return reinterpret_cast<T*>(m_manager->mapBuffer(m_handle, offset * sizeof(T), count * sizeof(T)));
 		}
 
 		void unmap() {
-			m_manager->unmapBuffer(m_handle_id);
-		}
-		
-		static Buffer<T> create(BufferManager* manager, BufferType type, size_t count, BufferMemoryType memoryType) {
-			return Buffer<T>(manager, manager->createBuffer(type, count * sizeof(T), memoryType), type, count, memoryType);
+			m_manager->unmapBuffer(m_handle);
 		}
 
 	private:
 		BufferManager* const m_manager;
-		const uint64_t m_handle_id;
+		const BufferHandle m_handle;
 		const BufferType m_type;
 		const size_t m_count;
 		const BufferMemoryType m_memoryType;
 		
-		Buffer<T>(BufferManager* manager, uint64_t id, BufferType type, size_t count, BufferMemoryType memoryType) :
+		Buffer<T>(BufferManager* manager, BufferHandle handle, BufferType type, size_t count, BufferMemoryType memoryType) :
 				m_manager(manager),
-				m_handle_id(id),
+				m_handle(handle),
 				m_type(type),
 				m_count(count),
 				m_memoryType(memoryType)
 		{}
-
-		vk::Buffer getVulkanHandle() const {
-			return m_manager->getBuffer(m_handle_id);
+		
+		[[nodiscard]]
+		static Buffer<T> create(BufferManager* manager, BufferType type, size_t count, BufferMemoryType memoryType) {
+			return Buffer<T>(manager, manager->createBuffer(type, count * sizeof(T), memoryType), type, count, memoryType);
 		}
+		
 	};
 }
diff --git a/include/vkcv/BufferManager.hpp b/include/vkcv/BufferManager.hpp
index abda720f98f649d64b17a15848ae99320e11ae3e..52e338483af7ddd01b6345b4ff1d0187ae4fbd8a 100644
--- a/include/vkcv/BufferManager.hpp
+++ b/include/vkcv/BufferManager.hpp
@@ -3,6 +3,8 @@
 #include <vector>
 #include <vulkan/vulkan.hpp>
 
+#include "Handles.hpp"
+
 namespace vkcv
 {
 	enum class BufferType {
@@ -36,7 +38,7 @@ namespace vkcv
 		
 		Core* m_core;
 		std::vector<Buffer> m_buffers;
-		uint64_t m_stagingBuffer;
+		BufferHandle m_stagingBuffer;
 		
 		BufferManager() noexcept;
 		
@@ -58,67 +60,67 @@ namespace vkcv
 		 * @param type Type of buffer
 		 * @param size Size of buffer in bytes
 		 * @param memoryType Type of buffers memory
-		 * @return New buffer handle id
+		 * @return New buffer handle
 		 */
-		uint64_t createBuffer(BufferType type, size_t size, BufferMemoryType memoryType);
+		BufferHandle createBuffer(BufferType type, size_t size, BufferMemoryType memoryType);
 		
 		/**
 		 * Returns the Vulkan buffer handle of a buffer
 		 * represented by a given buffer handle id.
 		 *
-		 * @param id Buffer handle id
+		 * @param handle Buffer handle
 		 * @return Vulkan buffer handle
 		 */
 		[[nodiscard]]
-		vk::Buffer getBuffer(uint64_t id) const;
+		vk::Buffer getBuffer(const BufferHandle& handle) const;
 		
 		/**
 		 * Returns the Vulkan device memory handle of a buffer
 		 * represented by a given buffer handle id.
 		 *
-		 * @param id Buffer handle id
+		 * @param handle Buffer handle
 		 * @return Vulkan device memory handle
 		 */
 		[[nodiscard]]
-		vk::DeviceMemory getDeviceMemory(uint64_t id) const;
+		vk::DeviceMemory getDeviceMemory(const BufferHandle& handle) const;
 		
 		/**
 		 * Fills a buffer represented by a given buffer
 		 * handle id with custom data.
 		 *
-		 * @param id Buffer handle id
+		 * @param handle Buffer handle
 		 * @param data Pointer to data
 		 * @param size Size of data in bytes
 		 * @param offset Offset to fill in data in bytes
 		 */
-		void fillBuffer(uint64_t id, void* data, size_t size, size_t offset);
+		void fillBuffer(const BufferHandle& handle, void* data, size_t size, size_t offset);
 		
 		/**
 		 * Maps memory to a buffer represented by a given
 		 * buffer handle id and returns it.
 		 *
-		 * @param id Buffer handle id
+		 * @param handle Buffer handle
 		 * @param offset Offset of mapping in bytes
 		 * @param size Size of mapping in bytes
 		 * @return Pointer to mapped memory
 		 */
-		void* mapBuffer(uint64_t id, size_t offset, size_t size);
+		void* mapBuffer(const BufferHandle& handle, size_t offset, size_t size);
 		
 		/**
 		 * Unmaps memory from a buffer represented by a given
 		 * buffer handle id.
 		 *
-		 * @param id Buffer handle id
+		 * @param handle Buffer handle
 		 */
-		void unmapBuffer(uint64_t id);
+		void unmapBuffer(const BufferHandle& handle);
 	
 		/**
 		 * Destroys and deallocates buffer represented by a given
 		 * buffer handle id.
 		 *
-		 * @param id Buffer handle id
+		 * @param handle Buffer handle
 		 */
-		void destroyBuffer(uint64_t id);
+		void destroyBuffer(const BufferHandle& handle);
 		
 	};
 	
diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp
index ec09e325efe2cecdc559756b2e72a07717cad121..3ff2d925bff188a585f6bd3047ba0e7b2ca594c3 100644
--- a/include/vkcv/Core.hpp
+++ b/include/vkcv/Core.hpp
@@ -188,7 +188,7 @@ namespace vkcv
 		*/
 		void renderTriangle(const PassHandle renderpassHandle, const PipelineHandle pipelineHandle,
 			const int width, const int height, const size_t pushConstantSize, const void* pushConstantData, 
-			const Buffer<float> &vertexBuffer);
+			const BufferHandle vertexBuffer);
 
 		/**
 		 * @brief end recording and present image
diff --git a/projects/first_triangle/src/main.cpp b/projects/first_triangle/src/main.cpp
index 6ed43eaa2a1e1c3f80029875502741835fdb7027..6261b4d8871e0ac6c2581eb97126a94adf100f5a 100644
--- a/projects/first_triangle/src/main.cpp
+++ b/projects/first_triangle/src/main.cpp
@@ -40,16 +40,15 @@ int main(int argc, const char** argv) {
 	
 	const size_t n = 5027;
 	
-	auto vertexBuffer = core.createBuffer<float>(vkcv::BufferType::VERTEX, n, vkcv::BufferMemoryType::DEVICE_LOCAL);
-	float vec_data [n*3];
-	
-	for (int i = 0; i < n; i += 3) {
-		vec_data[i] = 42;
-		vec_data[i + 1] = static_cast<float>(i);
-		vec_data[i + 2] = 7;
-	};
-	
+	auto vertexBuffer = core.createBuffer<vec3>(vkcv::BufferType::VERTEX, n, vkcv::BufferMemoryType::DEVICE_LOCAL);
+	vec3 vec_data[n];
+
+	for (size_t i = 0; i < n; i++) {
+		vec_data[i] = { 42, static_cast<float>(i), 7 };
+	}
+
 	vertexBuffer.fill(vec_data);
+
 	
 	/*vec3* m = buffer.map();
 	m[0] = { 0, 0, 0 };
@@ -144,7 +143,7 @@ int main(int argc, const char** argv) {
         cameraManager.getCamera().updateView(std::chrono::duration<double>(deltatime).count());
 		const glm::mat4 mvp = cameraManager.getCamera().getProjection() * cameraManager.getCamera().getView();
 
-	    core.renderTriangle(trianglePass, trianglePipeline, windowWidth, windowHeight, sizeof(mvp), &mvp, vertexBuffer);
+	    core.renderTriangle(trianglePass, trianglePipeline, windowWidth, windowHeight, sizeof(mvp), &mvp, vertexBuffer.getHandle());
 	    core.endFrame();
 	}
 	return 0;
diff --git a/src/vkcv/BufferManager.cpp b/src/vkcv/BufferManager.cpp
index cb7c7356aef589ee8aef03b904d48021bdb75057..e9b5112888782ae5be373f8a63777b7fc3503713 100644
--- a/src/vkcv/BufferManager.cpp
+++ b/src/vkcv/BufferManager.cpp
@@ -9,7 +9,7 @@
 namespace vkcv {
 	
 	BufferManager::BufferManager() noexcept :
-		m_core(nullptr), m_buffers(), m_stagingBuffer(UINT64_MAX)
+		m_core(nullptr), m_buffers(), m_stagingBuffer(BufferHandle{ UINT64_MAX })
 	{
 	}
 	
@@ -23,7 +23,7 @@ namespace vkcv {
 	
 	BufferManager::~BufferManager() noexcept {
 		for (size_t id = 0; id < m_buffers.size(); id++) {
-			destroyBuffer(id);
+			destroyBuffer(BufferHandle{ id });
 		}
 	}
 	
@@ -52,7 +52,7 @@ namespace vkcv {
 		return memoryTypeIndex;
 	}
 	
-	uint64_t BufferManager::createBuffer(BufferType type, size_t size, BufferMemoryType memoryType) {
+	BufferHandle BufferManager::createBuffer(BufferType type, size_t size, BufferMemoryType memoryType) {
 		vk::BufferCreateFlags createFlags;
 		vk::BufferUsageFlags usageFlags;
 		
@@ -115,7 +115,7 @@ namespace vkcv {
 		
 		const uint64_t id = m_buffers.size();
 		m_buffers.push_back({ buffer, memory, size, nullptr, mappable });
-		return id;
+		return BufferHandle{ id };
 	}
 	
 	struct StagingStepInfo {
@@ -178,7 +178,9 @@ namespace vkcv {
 		);
 	}
 	
-	vk::Buffer BufferManager::getBuffer(uint64_t id) const {
+	vk::Buffer BufferManager::getBuffer(const BufferHandle& handle) const {
+		const uint64_t id = handle.id;
+		
 		if (id >= m_buffers.size()) {
 			return nullptr;
 		}
@@ -188,7 +190,9 @@ namespace vkcv {
 		return buffer.m_handle;
 	}
 	
-	vk::DeviceMemory BufferManager::getDeviceMemory(uint64_t id) const {
+	vk::DeviceMemory BufferManager::getDeviceMemory(const BufferHandle& handle) const {
+		const uint64_t id = handle.id;
+		
 		if (id >= m_buffers.size()) {
 			return nullptr;
 		}
@@ -198,7 +202,9 @@ namespace vkcv {
 		return buffer.m_memory;
 	}
 	
-	void BufferManager::fillBuffer(uint64_t id, void *data, size_t size, size_t offset) {
+	void BufferManager::fillBuffer(const BufferHandle& handle, void *data, size_t size, size_t offset) {
+		const uint64_t id = handle.id;
+		
 		if (size == 0) {
 			size = SIZE_MAX;
 		}
@@ -226,7 +232,7 @@ namespace vkcv {
 			memcpy(mapped, data, max_size);
 			device.unmapMemory(buffer.m_memory);
 		} else {
-			auto& stagingBuffer = m_buffers[m_stagingBuffer];
+			auto& stagingBuffer = m_buffers[ m_stagingBuffer.id ];
 			
 			StagingStepInfo info;
 			info.data = data;
@@ -246,7 +252,9 @@ namespace vkcv {
 		}
 	}
 	
-	void* BufferManager::mapBuffer(uint64_t id, size_t offset, size_t size) {
+	void* BufferManager::mapBuffer(const BufferHandle& handle, size_t offset, size_t size) {
+		const uint64_t id = handle.id;
+		
 		if (size == 0) {
 			size = SIZE_MAX;
 		}
@@ -272,7 +280,9 @@ namespace vkcv {
 		return buffer.m_mapped;
 	}
 	
-	void BufferManager::unmapBuffer(uint64_t id) {
+	void BufferManager::unmapBuffer(const BufferHandle& handle) {
+		const uint64_t id = handle.id;
+		
 		if (id >= m_buffers.size()) {
 			return;
 		}
@@ -289,7 +299,9 @@ namespace vkcv {
 		buffer.m_mapped = nullptr;
 	}
 	
-	void BufferManager::destroyBuffer(uint64_t id) {
+	void BufferManager::destroyBuffer(const BufferHandle& handle) {
+		const uint64_t id = handle.id;
+		
 		if (id >= m_buffers.size()) {
 			return;
 		}
diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp
index 155b500e867774823fd41789e6b47f9eb900cb91..bac12602871a7605cba1d808fde1b21d03ade9bc 100644
--- a/src/vkcv/Core.cpp
+++ b/src/vkcv/Core.cpp
@@ -164,7 +164,7 @@ namespace vkcv
 
 	void Core::renderTriangle(const PassHandle renderpassHandle, const PipelineHandle pipelineHandle, 
 		const int width, const int height, const size_t pushConstantSize, const void *pushConstantData,
-		const Buffer<float> &vertexBuffer) {
+		const BufferHandle vertexBuffer) {
 
 		if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) {
 			return;
@@ -175,7 +175,7 @@ namespace vkcv
 		const vk::Pipeline pipeline		= m_PipelineManager->getVkPipeline(pipelineHandle);
         const vk::PipelineLayout pipelineLayout = m_PipelineManager->getVkPipelineLayout(pipelineHandle);
 		const vk::Rect2D renderArea(vk::Offset2D(0, 0), vk::Extent2D(width, height));
-		const vk::Buffer vulkanVertexBuffer = vertexBuffer.getVulkanHandle();
+		const vk::Buffer vulkanVertexBuffer = m_BufferManager->getBuffer(vertexBuffer);
 
 		const vk::Framebuffer framebuffer = createFramebuffer(m_Context.getDevice(), renderpass, width, height, imageView);
 		m_TemporaryFramebuffers.push_back(framebuffer);