diff --git a/extern/glow-extras b/extern/glow-extras
index 9a73f0ea1a1272d861fd329d8f61f24cb74d536b..48afcb1fdbcaf874f7d58d86545525bca554696c 160000
--- a/extern/glow-extras
+++ b/extern/glow-extras
@@ -1 +1 @@
-Subproject commit 9a73f0ea1a1272d861fd329d8f61f24cb74d536b
+Subproject commit 48afcb1fdbcaf874f7d58d86545525bca554696c
diff --git a/extern/imgui b/extern/imgui
index fcf73e7562f82dca7de2e66666e77cbbcc38be5b..8ca0ab36f8a5072b8dfaf569646233166f507c84 160000
--- a/extern/imgui
+++ b/extern/imgui
@@ -1 +1 @@
-Subproject commit fcf73e7562f82dca7de2e66666e77cbbcc38be5b
+Subproject commit 8ca0ab36f8a5072b8dfaf569646233166f507c84
diff --git a/samples/basic/viewer/main.cc b/samples/basic/viewer/main.cc
index 86fa731a5c47ffc79ec4e9de816179709422a5c5..9033679864195197ab597e3d2319084b791e6c6f 100644
--- a/samples/basic/viewer/main.cc
+++ b/samples/basic/viewer/main.cc
@@ -1,4 +1,5 @@
 #include <imgui/imgui.h>
+#include <imgui/imguizmo.h>
 
 #include <glow/common/str_utils.hh>
 #include <glow/data/TextureData.hh>
@@ -15,6 +16,8 @@
 #include <glow-extras/viewer/canvas.hh>
 #include <glow-extras/viewer/view.hh>
 
+#include <GLFW/glfw3.h>
+
 #include <typed-geometry/tg.hh>
 
 // path to sample files
@@ -164,6 +167,117 @@ void advanced_objects(pm::vertex_attribute<tg::pos3> const& pos)
     // TODO: images
 }
 
+void imguizmo(pm::vertex_attribute<tg::pos3> const& pos)
+{
+    // This sample shows how to use ImGuizmo, a very useful addon for ImGui for editing 4x4 affine transformation matrices using the viewer
+    // For additional information refer to https://github.com/CedricGuillemet/ImGuizmo/
+
+    // your own camera is required to access the camera's view and projection matrix
+    auto cam = gv::CameraController::create();
+
+    // cache the renderable
+    auto r = gv::make_renderable(pos);
+
+    // local vs global coordinate system
+    auto currentGizmoMode = ImGuizmo::LOCAL;
+
+    // the operation that we want to perform (rotate, scale, or translate)
+    auto currentGizmoOperation = ImGuizmo::ROTATE;
+
+    // do we want to snap to specific values
+    bool useSnap = false;
+
+    // the values that we want to snap to, i.e. 1 degree steps
+    float snap[3] = {1.f, 1.f, 1.f};
+
+    // the transformation that we want to edit
+    auto transform = tg::mat4::identity;
+
+    // an interactive view is required
+    gv::interactive([&](float) {
+        // you can define keybindings to select an operation
+        if (ImGui::IsKeyPressed(GLFW_KEY_T))
+            currentGizmoOperation = ImGuizmo::TRANSLATE;
+        if (ImGui::IsKeyPressed(GLFW_KEY_R))
+            currentGizmoOperation = ImGuizmo::ROTATE;
+        if (ImGui::IsKeyPressed(GLFW_KEY_S))
+            currentGizmoOperation = ImGuizmo::SCALE;
+
+        // you can also choose the correct operation using gui elements
+        if (ImGui::RadioButton("Translate", currentGizmoOperation == ImGuizmo::TRANSLATE))
+            currentGizmoOperation = ImGuizmo::TRANSLATE;
+        ImGui::SameLine();
+        if (ImGui::RadioButton("Rotate", currentGizmoOperation == ImGuizmo::ROTATE))
+            currentGizmoOperation = ImGuizmo::ROTATE;
+        ImGui::SameLine();
+        if (ImGui::RadioButton("Scale", currentGizmoOperation == ImGuizmo::SCALE))
+            currentGizmoOperation = ImGuizmo::SCALE;
+
+        // ImGuizmo decomposes your input transform into translation, rotation, and scaling components
+        float matrixTranslation[3], matrixRotation[3], matrixScale[3];
+        ImGuizmo::DecomposeMatrixToComponents(&transform[0][0], matrixTranslation, matrixRotation, matrixScale);
+
+        // show the three components individually
+        ImGui::InputFloat3("Tr", matrixTranslation);
+        ImGui::InputFloat3("Rt", matrixRotation);
+        ImGui::InputFloat3("Sc", matrixScale);
+
+        // rebuild your 4x4 transform matrix
+        ImGuizmo::RecomposeMatrixFromComponents(matrixTranslation, matrixRotation, matrixScale, &transform[0][0]);
+
+        // only show the radio button to choose local/global coordinate system when NOT scaling
+        // as scaling is always in local coordinates
+        if (currentGizmoOperation != ImGuizmo::SCALE)
+        {
+            if (ImGui::RadioButton("Local", currentGizmoMode == ImGuizmo::LOCAL))
+                currentGizmoMode = ImGuizmo::LOCAL;
+            ImGui::SameLine();
+            if (ImGui::RadioButton("World", currentGizmoMode == ImGuizmo::WORLD))
+                currentGizmoMode = ImGuizmo::WORLD;
+        }
+
+        // toggle snap on p key
+        if (ImGui::IsKeyPressed(GLFW_KEY_P))
+            useSnap = !useSnap;
+
+        // ... or use gui elements
+        ImGui::Checkbox("", &useSnap);
+        ImGui::SameLine();
+
+        // name the snap input accordingly
+        switch (currentGizmoOperation)
+        {
+        case ImGuizmo::TRANSLATE:
+            ImGui::InputFloat3("Snap", snap);
+            break;
+        case ImGuizmo::ROTATE:
+            ImGui::InputFloat("Angle Snap", snap);
+            break;
+        case ImGuizmo::SCALE:
+            ImGui::InputFloat("Scale Snap", snap);
+            break;
+        default:   // silence warnings as there are quite a few more possible operations supported by ImGuizmo
+            break; // do nothing
+        }
+
+        // ImGuizmo requires view and projection matrix for rendering.
+        // However, it does not work with a reversed z projection matrix, which is used by the viewer by default
+        // So we need to disable it temporarily, generate a projection matrix without it, and then reactivate it
+        auto view = cam->computeViewMatrix();
+        auto const rev_z_enabled = cam->reverseZEnabled();
+        cam->setReverseZEnabled(false); //
+        auto project = cam->computeProjMatrix();
+        cam->setReverseZEnabled(rev_z_enabled);
+
+        // This is where all the ImGuizmo magic comes together
+        ImGuizmo::Manipulate(&view[0][0], &project[0][0], currentGizmoOperation, currentGizmoMode, &transform[0][0], nullptr, useSnap ? &snap[0] : nullptr);
+
+        // Because the view is resized when we change the object, it might be a good idea to pass the viewer a fixed bounding box
+        auto const aabb = tg::aabb3(tg::pos3(-2), tg::pos3(2));
+        gv::view(r, cam, transform, aabb);
+    });
+}
+
 void advanced_visualization(pm::vertex_attribute<tg::pos3> const& pos)
 {
     // the args in gv::view(obj, args) do not only configure the viewer but also change how the renderable is visualized
@@ -1011,6 +1125,8 @@ int main()
         subtle_cases(pos);
 
         headless_screenshot(pos);
+
+        imguizmo(pos);
     }
 
     // known issues