diff --git a/src/main.cpp b/src/main.cpp
index fb6b964..ee52ca8 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -81,6 +81,7 @@ private:
     vk::raii::Queue m_presentQueue{ nullptr };
     std::vector<Vertex> m_vertices;
     std::vector<InstanceData> m_instanceDatas;
+    std::vector<glm::mat4> m_dynamicUboMatrices;
     std::vector<uint32_t> m_indices;
     std::vector<uint32_t> m_firstIndices;
     vk::raii::DeviceMemory m_vertexBufferMemory{ nullptr };
@@ -92,6 +93,9 @@ private:
     std::vector<vk::raii::DeviceMemory> m_uniformBuffersMemory;
     std::vector<vk::raii::Buffer> m_uniformBuffers;
     std::vector<void*> m_uniformBuffersMapped;
+    std::vector<vk::raii::DeviceMemory> m_dynamicUniformBuffersMemory;
+    std::vector<vk::raii::Buffer> m_dynamicUniformBuffers;
+    std::vector<void*> m_dynamicUniformBuffersMapped;
     vk::raii::DeviceMemory m_textureImageMemory{ nullptr };
     uint32_t m_mipLevels;
     vk::raii::Image m_textureImage{ nullptr };
@@ -164,10 +168,12 @@ private:
         loadModel(MODEL_PATH);
         loadModel(BUNNY_PATH);
         initInstanceDatas();
+        initDynamicUboMatrices();
         createVertexBuffer();
         createInstanceBuffer();
         createIndexBuffer();
         createUniformBuffers();
+        createDynamicUniformBuffers();
         createDescriptorPool();
         createDescriptorSets();
         createCommandBuffers();
@@ -184,6 +190,10 @@ private:
     }
 
     void cleanup() {
+        for(const auto& it : m_dynamicUniformBuffersMemory){
+            it.unmapMemory();
+        }
+
         for(const auto& it : m_uniformBuffersMemory){
             it.unmapMemory();
         }
@@ -833,14 +843,14 @@ private:
         commandBuffer.bindVertexBuffers( 0, vertexBuffers, offsets );
         commandBuffer.bindIndexBuffer( m_indexBuffer, 0, vk::IndexType::eUint32 );
 
+        uint32_t dynamicOffset = 0;
         commandBuffer.bindDescriptorSets(
             vk::PipelineBindPoint::eGraphics, 
             m_pipelineLayout,
             0,
             *m_descriptorSets[m_currentFrame],
-            nullptr
+            dynamicOffset
         );
-
         uint32_t enableTexture = 1; // enable texture
         commandBuffer.pushConstants<uint32_t>(
             m_pipelineLayout,
@@ -856,6 +866,14 @@ private:
             0
         );
 
+        dynamicOffset = sizeof(glm::mat4);
+        commandBuffer.bindDescriptorSets(
+            vk::PipelineBindPoint::eGraphics, 
+            m_pipelineLayout,
+            0,
+            *m_descriptorSets[m_currentFrame],
+            dynamicOffset
+        );
         enableTexture = 0; // disable texture for the bunny
         commandBuffer.pushConstants<uint32_t>(
             m_pipelineLayout,
@@ -910,6 +928,7 @@ private:
         m_device.resetFences( *m_inFlightFences[m_currentFrame] );
 
         updateUniformBuffer(m_currentFrame);
+        updateDynamicUniformBuffer(m_currentFrame);
 
         m_commandBuffers[m_currentFrame].reset();
         recordCommandBuffer(m_commandBuffers[m_currentFrame], imageIndex);
@@ -1150,7 +1169,13 @@ private:
         samplerLayoutBinding.descriptorCount = 1;
         samplerLayoutBinding.stageFlags = vk::ShaderStageFlagBits::eFragment;
 
-        std::array<vk::DescriptorSetLayoutBinding, 2> bindings{ uboLayoutBinding, samplerLayoutBinding };
+        vk::DescriptorSetLayoutBinding dynamicUboLayoutBinding;
+        dynamicUboLayoutBinding.binding = 2;
+        dynamicUboLayoutBinding.descriptorType = vk::DescriptorType::eUniformBufferDynamic;
+        dynamicUboLayoutBinding.descriptorCount = 1;
+        dynamicUboLayoutBinding.stageFlags = vk::ShaderStageFlagBits::eVertex;
+
+        auto bindings = { uboLayoutBinding, samplerLayoutBinding, dynamicUboLayoutBinding };
         vk::DescriptorSetLayoutCreateInfo layoutInfo;
         layoutInfo.setBindings( bindings );
 
@@ -1209,12 +1234,17 @@ private:
     /////////////////////////////////////////////////////////////////
     /// descriptor pool and sets
     void createDescriptorPool() {
-        std::array<vk::DescriptorPoolSize, 2> poolSizes;
+        std::array<vk::DescriptorPoolSize, 3> poolSizes;
+
         poolSizes[0].type = vk::DescriptorType::eUniformBuffer;
         poolSizes[0].descriptorCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
+
         poolSizes[1].type = vk::DescriptorType::eCombinedImageSampler;
         poolSizes[1].descriptorCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
 
+        poolSizes[2].type = vk::DescriptorType::eUniformBufferDynamic;
+        poolSizes[2].descriptorCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
+
         vk::DescriptorPoolCreateInfo poolInfo;
         poolInfo.flags = vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet;
         poolInfo.setPoolSizes( poolSizes );
@@ -1241,18 +1271,30 @@ private:
             imageInfo.imageView = m_textureImageView;
             imageInfo.sampler = m_textureSampler;
 
-            std::array<vk::WriteDescriptorSet, 2> descriptorWrites;
+            vk::DescriptorBufferInfo dynamicBufferInfo;
+            dynamicBufferInfo.buffer = m_dynamicUniformBuffers[i];
+            dynamicBufferInfo.offset = 0;
+            dynamicBufferInfo.range = sizeof(glm::mat4);
+
+            std::array<vk::WriteDescriptorSet, 3> descriptorWrites;
             descriptorWrites[0].dstSet = m_descriptorSets[i];
             descriptorWrites[0].dstBinding = 0;
             descriptorWrites[0].dstArrayElement = 0;
             descriptorWrites[0].descriptorType = vk::DescriptorType::eUniformBuffer;
             descriptorWrites[0].setBufferInfo(bufferInfo);
+
             descriptorWrites[1].dstSet = m_descriptorSets[i];
             descriptorWrites[1].dstBinding = 1;
             descriptorWrites[1].dstArrayElement = 0;
             descriptorWrites[1].descriptorType = vk::DescriptorType::eCombinedImageSampler;
             descriptorWrites[1].setImageInfo(imageInfo);
 
+            descriptorWrites[2].dstSet = m_descriptorSets[i];
+            descriptorWrites[2].dstBinding = 2;
+            descriptorWrites[2].dstArrayElement = 0;
+            descriptorWrites[2].descriptorType = vk::DescriptorType::eUniformBufferDynamic;
+            descriptorWrites[2].setBufferInfo(dynamicBufferInfo);
+
             m_device.updateDescriptorSets(descriptorWrites, nullptr);
         }
     }
@@ -1965,6 +2007,54 @@ private:
         copyBuffer(stagingBuffer, m_instanceBuffer, bufferSize);
     }
     /////////////////////////////////////////////////////////////////
+
+    /////////////////////////////////////////////////////////////////
+    /// dynamic uniform buffer
+    void initDynamicUboMatrices() {
+        m_dynamicUboMatrices.push_back(glm::mat4(1.0f));
+        m_dynamicUboMatrices.push_back(glm::mat4(1.0f));
+    }
+    void createDynamicUniformBuffers() {
+        vk::DeviceSize bufferSize  = sizeof(glm::mat4) * m_dynamicUboMatrices.size();
+
+        m_dynamicUniformBuffers.reserve(MAX_FRAMES_IN_FLIGHT);
+        m_dynamicUniformBuffersMemory.reserve(MAX_FRAMES_IN_FLIGHT);
+        m_dynamicUniformBuffersMapped.reserve(MAX_FRAMES_IN_FLIGHT);
+
+        for(size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
+            m_dynamicUniformBuffers.emplace_back( nullptr );
+            m_dynamicUniformBuffersMemory.emplace_back( nullptr );
+            m_dynamicUniformBuffersMapped.emplace_back( nullptr );
+            createBuffer(bufferSize, 
+                vk::BufferUsageFlagBits::eUniformBuffer, 
+                vk::MemoryPropertyFlagBits::eHostVisible | 
+                vk::MemoryPropertyFlagBits::eHostCoherent, 
+                m_dynamicUniformBuffers[i], 
+                m_dynamicUniformBuffersMemory[i]
+            );
+
+            m_dynamicUniformBuffersMapped[i] = m_dynamicUniformBuffersMemory[i].mapMemory(0, bufferSize);
+        }
+    }
+    void updateDynamicUniformBuffer(uint32_t currentImage) {
+        static auto startTime = std::chrono::high_resolution_clock::now();
+        auto currentTime = std::chrono::high_resolution_clock::now();
+        float time = std::chrono::duration<float, std::chrono::seconds::period>(currentTime - startTime).count();
+        startTime = currentTime;
+
+        m_dynamicUboMatrices[1] = glm::rotate(
+            m_dynamicUboMatrices[1], 
+            glm::radians(time * 60.0f), 
+            glm::vec3(0.0f, 1.0f, 0.0f)
+        );
+
+        memcpy(
+            m_dynamicUniformBuffersMapped[currentImage],
+            m_dynamicUboMatrices.data(),
+            sizeof(glm::mat4) * m_dynamicUboMatrices.size()
+        );
+    }
+    /////////////////////////////////////////////////////////////////
 };
 
 
