diff --git a/.gitignore b/.gitignore
index 8017f59..81f5c0f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,4 +13,7 @@ CMakeLists.txt.user*
 
 # Clangd
 /compile_commands.json
-/.cache
\ No newline at end of file
+/.cache
+
+# Gdb
+.gdb_history
\ No newline at end of file
diff --git a/core/src/datatypes/cframe.cpp b/core/src/datatypes/cframe.cpp
index 9ea6199..791f308 100644
--- a/core/src/datatypes/cframe.cpp
+++ b/core/src/datatypes/cframe.cpp
@@ -33,7 +33,7 @@ Data::CFrame::CFrame(const rp::Transform& transform) : Data::CFrame::CFrame(rpTo
 }
 
 glm::mat3 lookAt(Data::Vector3 position, Data::Vector3 lookAt, Data::Vector3 up) {
-    // https://github.com/sgorsten/glm/issues/29#issuecomment-743989030
+    // https://github.com/sgorsten/linalg/issues/29#issuecomment-743989030
 	Data::Vector3 f = (lookAt - position).Unit(); // Forward/Look
 	Data::Vector3 u = up.Unit(); // Up
 	Data::Vector3 s = f.Cross(u).Unit(); // Right
diff --git a/core/src/math_helper.cpp b/core/src/math_helper.cpp
new file mode 100644
index 0000000..3d8a4b3
--- /dev/null
+++ b/core/src/math_helper.cpp
@@ -0,0 +1,103 @@
+#include "math_helper.h"
+
+#define CMP_EPSILON 0.00001
+
+// After a long time researching, I was able to use and adapt Godot's implementation of movable handles (godot/editor/plugins/gizmos/gizmo_3d_helper.cpp)
+// All thanks goes to them and David Eberly for his algorithm.
+
+void get_closest_points_between_segments(const glm::vec3 &p_p0, const glm::vec3 &p_p1, const glm::vec3 &p_q0, const glm::vec3 &p_q1, glm::vec3 &r_ps, glm::vec3 &r_qt) {
+    // Based on David Eberly's Computation of Distance Between Line Segments algorithm.
+
+	glm::vec3 p = p_p1 - p_p0;
+	glm::vec3 q = p_q1 - p_q0;
+	glm::vec3 r = p_p0 - p_q0;
+
+	float a = glm::dot(p, p);
+	float b = glm::dot(p, q);
+	float c = glm::dot(q, q);
+	float d = glm::dot(p, r);
+	float e = glm::dot(q, r);
+
+	float s = 0.0f;
+	float t = 0.0f;
+
+	float det = a * c - b * b;
+	if (det > CMP_EPSILON) {
+		// Non-parallel segments
+		float bte = b * e;
+		float ctd = c * d;
+
+		if (bte <= ctd) {
+			// s <= 0.0f
+			if (e <= 0.0f) {
+				// t <= 0.0f
+				s = (-d >= a ? 1 : (-d > 0.0f ? -d / a : 0.0f));
+				t = 0.0f;
+			} else if (e < c) {
+				// 0.0f < t < 1
+				s = 0.0f;
+				t = e / c;
+			} else {
+				// t >= 1
+				s = (b - d >= a ? 1 : (b - d > 0.0f ? (b - d) / a : 0.0f));
+				t = 1;
+			}
+		} else {
+			// s > 0.0f
+			s = bte - ctd;
+			if (s >= det) {
+				// s >= 1
+				if (b + e <= 0.0f) {
+					// t <= 0.0f
+					s = (-d <= 0.0f ? 0.0f : (-d < a ? -d / a : 1));
+					t = 0.0f;
+				} else if (b + e < c) {
+					// 0.0f < t < 1
+					s = 1;
+					t = (b + e) / c;
+				} else {
+					// t >= 1
+					s = (b - d <= 0.0f ? 0.0f : (b - d < a ? (b - d) / a : 1));
+					t = 1;
+				}
+			} else {
+				// 0.0f < s < 1
+				float ate = a * e;
+				float btd = b * d;
+
+				if (ate <= btd) {
+					// t <= 0.0f
+					s = (-d <= 0.0f ? 0.0f : (-d >= a ? 1 : -d / a));
+					t = 0.0f;
+				} else {
+					// t > 0.0f
+					t = ate - btd;
+					if (t >= det) {
+						// t >= 1
+						s = (b - d <= 0.0f ? 0.0f : (b - d >= a ? 1 : (b - d) / a));
+						t = 1;
+					} else {
+						// 0.0f < t < 1
+						s /= det;
+						t /= det;
+					}
+				}
+			}
+		}
+	} else {
+		// Parallel segments
+		if (e <= 0.0f) {
+			s = (-d <= 0.0f ? 0.0f : (-d >= a ? 1 : -d / a));
+			t = 0.0f;
+		} else if (e >= c) {
+			s = (b - d <= 0.0f ? 0.0f : (b - d >= a ? 1 : (b - d) / a));
+			t = 1;
+		} else {
+			s = 0.0f;
+			t = e / c;
+		}
+	}
+
+	r_ps = (1 - s) * p_p0 + s * p_p1;
+	r_qt = (1 - t) * p_q0 + t * p_q1;
+}
\ No newline at end of file
diff --git a/core/src/math_helper.h b/core/src/math_helper.h
new file mode 100644
index 0000000..65c316c
--- /dev/null
+++ b/core/src/math_helper.h
@@ -0,0 +1,5 @@
+#pragma once
+#include <glm/glm.hpp>
+
+// From godot/editor/plugins/gizmos/gizmo_3d_helper.h
+void get_closest_points_between_segments(const glm::vec3 &p_p0, const glm::vec3 &p_p1, const glm::vec3 &p_q0, const glm::vec3 &p_q1, glm::vec3 &r_ps, glm::vec3 &r_qt);
\ No newline at end of file
diff --git a/core/src/objects/handles.cpp b/core/src/objects/handles.cpp
index c1b58d6..69b571c 100644
--- a/core/src/objects/handles.cpp
+++ b/core/src/objects/handles.cpp
@@ -42,6 +42,15 @@ Data::CFrame Handles::GetCFrameOfHandle(HandleFace face) {
     return adornee->lock()->cframe + glm::vec3(glm::mat4(adornee->lock()->cframe.Rotation()) * glm::vec4((adornee->lock()->size * 0.5f * face.normal) + face.normal * 5.f, 0));
 }
 
+Data::CFrame Handles::PartCFrameFromHandlePos(HandleFace face, Data::Vector3 newPos) {
+    if (!adornee.has_value() || adornee->expired()) return Data::CFrame(glm::vec3(0,0,0), (Data::Vector3)glm::vec3(0,0,0));
+
+    // return adornee->lock()->cframe + face.normal * 5.f;
+    if (worldMode)
+        return adornee->lock()->cframe.Rotation() + newPos - (adornee->lock()->size * 0.5f * face.normal) - face.normal * 5.f;
+    return adornee->lock()->cframe.Rotation() + newPos - glm::vec3(glm::mat4(adornee->lock()->cframe.Rotation()) * glm::vec4((adornee->lock()->size * 0.5f * face.normal) + face.normal * 5.f, 0));
+}
+
 std::optional<HandleFace> Handles::RaycastHandle(rp3d::Ray ray) {
     for (HandleFace face : HandleFace::Faces) {
         Data::CFrame cframe = GetCFrameOfHandle(face);
diff --git a/core/src/objects/handles.h b/core/src/objects/handles.h
index ac9860d..e4c7885 100644
--- a/core/src/objects/handles.h
+++ b/core/src/objects/handles.h
@@ -37,6 +37,7 @@ public:
     // World-space handles vs local-space handles
     bool worldMode = false;
     Data::CFrame GetCFrameOfHandle(HandleFace face);
+    Data::CFrame PartCFrameFromHandlePos(HandleFace face, Data::Vector3 newPos);
     std::optional<HandleFace> RaycastHandle(rp3d::Ray ray);
 
     static inline std::shared_ptr<Handles> New() { return std::make_shared<Handles>(); };
diff --git a/core/src/rendering/renderer.cpp b/core/src/rendering/renderer.cpp
index cfcd800..f89cdd0 100644
--- a/core/src/rendering/renderer.cpp
+++ b/core/src/rendering/renderer.cpp
@@ -114,6 +114,7 @@ void renderParts() {
         if (inst->GetClass()->className != "Part") continue;
         std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(inst);
         glm::mat4 model = part->cframe;
+        if (inst->name == "camera") model = camera.getLookAt();
         model = glm::scale(model, part->size);
         shader->set("model", model);
         shader->set("material", Material {
@@ -196,8 +197,8 @@ void render(GLFWwindow* window) {
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
     renderSkyBox();
-    renderParts();
     renderHandles();
+    renderParts();
 }
 
 void setViewport(int width, int height) {
diff --git a/editor/mainglwidget.cpp b/editor/mainglwidget.cpp
index 85ccd4e..0168bd5 100644
--- a/editor/mainglwidget.cpp
+++ b/editor/mainglwidget.cpp
@@ -3,6 +3,7 @@
 
 #include <QMouseEvent>
 #include <glm/ext/matrix_projection.hpp>
+#include <glm/ext/matrix_transform.hpp>
 #include <glm/ext/vector_float3.hpp>
 #include <glm/geometric.hpp>
 #include <glm/matrix.hpp>
@@ -11,7 +12,9 @@
 #include <reactphysics3d/collision/RaycastInfo.h>
 #include <vector>
 
+#include "datatypes/cframe.h"
 #include "editorcommon.h"
+#include "objects/handles.h"
 #include "physics/util.h"
 #include "qcursor.h"
 #include "qevent.h"
@@ -25,6 +28,8 @@
 #include "rendering/shader.h"
 
 #include "mainglwidget.h"
+#include "../core/src/rendering/defaultmeshes.h"
+#include "math_helper.h"
 
 MainGLWidget::MainGLWidget(QWidget* parent): QOpenGLWidget(parent) {
     setFocusPolicy(Qt::FocusPolicy::ClickFocus);
@@ -55,8 +60,47 @@ glm::vec2 firstPoint;
 glm::vec2 secondPoint;
 
 extern std::optional<std::weak_ptr<Part>> draggingObject;
+extern std::optional<HandleFace> draggingHandle;
+extern Shader* shader;
 void MainGLWidget::paintGL() {
     ::render(NULL);
+
+    if (!editorToolHandles->adornee) return;
+
+    for (HandleFace face : HandleFace::Faces) {
+        // Ignore negatives (for now)
+        if (face.normal != glm::abs(face.normal)) continue;
+
+        glm::vec3 axisNormal = face.normal;
+        // // glm::vec3 planeNormal = camera.cameraFront;
+        glm::vec3 planeRight = glm::cross(axisNormal, glm::normalize(camera.cameraFront));
+        glm::vec3 planeNormal = glm::cross(glm::normalize(planeRight), axisNormal);
+
+        auto a = axisNormal;
+        auto b = planeRight;
+        auto c = planeNormal;
+
+        glm::mat3 matrix = {
+            axisNormal,
+            planeRight,
+            -planeNormal,
+        };
+
+        glm::mat4 model = glm::translate(glm::mat4(1.0f), (glm::vec3)editorToolHandles->adornee->lock()->position()) * glm::mat4(matrix);
+        model = glm::scale(model, glm::vec3(5, 5, 0.2));
+
+        shader->set("model", model);
+        shader->set("material", Material {
+            .diffuse = glm::vec3(0.5, 1.f, 0.5),
+            .specular = glm::vec3(0.5f, 0.5f, 0.5f),
+            .shininess = 16.0f,
+        });
+        glm::mat3 normalMatrix = glm::mat3(glm::transpose(glm::inverse(model)));
+        shader->set("normalMatrix", normalMatrix);
+
+        CUBE_MESH->bind();
+        // glDrawArrays(GL_TRIANGLES, 0, 36);
+    }
 }
 
 bool isMouseRightDragging = false;
@@ -90,6 +134,10 @@ void MainGLWidget::handleObjectDrag(QMouseEvent* evt) {
     syncPartPhysics(draggingObject->lock());
 }
 
+inline glm::vec3 vec3fy(glm::vec4 vec) {
+    return vec / vec.w;
+}
+
 QPoint lastPoint;
 void MainGLWidget::handleHandleDrag(QMouseEvent* evt) {
     QPoint cLastPoint = lastPoint;
@@ -97,24 +145,39 @@ void MainGLWidget::handleHandleDrag(QMouseEvent* evt) {
 
     if (!isMouseDragging || !draggingHandle || !editorToolHandles->adornee) return;
 
-    // https://stackoverflow.com/a/57380616/16255372
     QPoint position = evt->pos();
-    glm::mat4 projection = glm::perspective(glm::radians(45.f), (float)width() / (float)height(), 0.1f, 100.0f);
-    glm::mat4 view = camera.getLookAt();
-    glm::mat4 worldToScreen = projection * view;
-    glm::vec4 a = worldToScreen * glm::vec4((glm::vec3)editorToolHandles->adornee->lock()->position(), 1);
-    glm::vec4 b = worldToScreen * glm::vec4((glm::vec3)editorToolHandles->adornee->lock()->position() + draggingHandle->normal, 1);
-    glm::vec2 screenDir = b / b.w - a / a.w;
 
-    QPoint mouseDelta_ = evt->pos() - cLastPoint;
-    glm::vec2 mouseDelta(mouseDelta_.x(), mouseDelta_.y() * -1.f);
-    float changeBy = glm::dot(mouseDelta, screenDir);
+    // This was actually quite a difficult problem to solve, managing to get the handle to go underneath the cursor
+
+    glm::vec3 pointDir = camera.getScreenDirection(glm::vec2(position.x(), position.y()), glm::vec2(width(), height()));
+    pointDir = glm::normalize(pointDir);
+
+    Data::CFrame handleCFrame = editorToolHandles->GetCFrameOfHandle(draggingHandle.value());
+
+    // Segment from axis stretching -4096 to +4096 rel to handle's position 
+    glm::vec3 axisSegment0 = handleCFrame.Position() + glm::abs(draggingHandle->normal) * 4096.0f;
+    glm::vec3 axisSegment1 = handleCFrame.Position() + glm::abs(draggingHandle->normal) * -4096.0f;
+
+    // Segment from camera stretching 4096 forward
+    glm::vec3 mouseSegment0 = camera.cameraPos;
+    glm::vec3 mouseSegment1 = camera.cameraPos + pointDir * 4096.0f;
+
+    // Closest point on the axis segment between the two segments
+    glm::vec3 handlePoint, rb;
+    get_closest_points_between_segments(axisSegment0, axisSegment1, mouseSegment0, mouseSegment1, handlePoint, rb);
 
     if (selectedTool == SelectedTool::MOVE)
-        editorToolHandles->adornee->lock()->cframe = editorToolHandles->adornee->lock()->cframe + draggingHandle->normal * changeBy;
+        editorToolHandles->adornee->lock()->cframe = editorToolHandles->PartCFrameFromHandlePos(draggingHandle.value(), handlePoint);
     else if (selectedTool == SelectedTool::SCALE) {
-        if (!(evt->modifiers() & Qt::ControlModifier)) editorToolHandles->adornee->lock()->cframe = editorToolHandles->adornee->lock()->cframe + draggingHandle->normal * changeBy * 0.5f;
-        editorToolHandles->adornee->lock()->size += glm::abs(draggingHandle->normal) * changeBy;
+        glm::vec3 handlePos = editorToolHandles->PartCFrameFromHandlePos(draggingHandle.value(), handlePoint).Position();
+        
+        // Find change in handles, and negate difference in sign between axes
+        glm::vec3 diff = handlePos - glm::vec3(editorToolHandles->adornee->lock()->position());
+        editorToolHandles->adornee->lock()->size += diff * glm::sign(draggingHandle->normal);
+        
+        // If alt is not pressed, also reposition the part such that only the dragged side gets lengthened
+        if (!(evt->modifiers() & Qt::ControlModifier)) 
+            editorToolHandles->adornee->lock()->cframe = editorToolHandles->adornee->lock()->cframe + (diff / 2.0f);
     }
 
     syncPartPhysics(std::dynamic_pointer_cast<Part>(editorToolHandles->adornee->lock()));