diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 3db9c3351ab442c30de1699aafcf6cf84ab0544e..506ac54f9e14bc217e208a72cf675f5fb14742a7 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -8,6 +8,7 @@ include_directories(
     "${PROJECT_SOURCE_DIR}/src/common/include"
     "${PROJECT_SOURCE_DIR}/src/controls/include"
     "${PROJECT_SOURCE_DIR}/src/editor/include"
+    "${PROJECT_SOURCE_DIR}/src/algo/include"
     )
 
 set(ROFIBOTS_LIBRARIES_TO_LINK_WITH
@@ -19,6 +20,7 @@ set(ROFIBOTS_LIBRARIES_TO_LINK_WITH
     common
     controls
     editor
+    algo
     )
 
 message("Including the following libraries to the build:")
@@ -38,6 +40,8 @@ add_subdirectory(./controls)
 message("-- controls")  
 add_subdirectory(./editor)
 message("-- editor")  
+add_subdirectory(./algo)
+message("-- algo")  
 
 message("Including the following executables to the build:")
 add_subdirectory(./studio)
diff --git a/src/algo/CMakeLists.txt b/src/algo/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..9ebd3ef67b9cce39363892ee65d9c6ca3b5ba470
--- /dev/null
+++ b/src/algo/CMakeLists.txt
@@ -0,0 +1,15 @@
+set(THIS_TARGET_NAME algo)
+
+add_library(${THIS_TARGET_NAME}
+    ./include/algo/cursor.hpp
+    ./src/cursor.cpp
+
+    )
+
+set_target_properties(${THIS_TARGET_NAME} PROPERTIES
+    DEBUG_OUTPUT_NAME "${THIS_TARGET_NAME}_${CMAKE_SYSTEM_NAME}_Debug"
+    RELEASE_OUTPUT_NAME "${THIS_TARGET_NAME}_${CMAKE_SYSTEM_NAME}_Release"
+    RELWITHDEBINFO_OUTPUT_NAME "${THIS_TARGET_NAME}_${CMAKE_SYSTEM_NAME}_RelWithDebInfo"
+    )
+
+#install(TARGETS ${THIS_TARGET_NAME} DESTINATION "lib")
diff --git a/src/algo/include/algo/cursor.hpp b/src/algo/include/algo/cursor.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..28e02897db2da76887e90ca78006a833ba87d654
--- /dev/null
+++ b/src/algo/include/algo/cursor.hpp
@@ -0,0 +1,23 @@
+#ifndef CURSOR_INCLUDED
+#define CURSOR_INCLUDED
+
+#include <common/node.hpp>
+#include <editor/scene.hpp>
+#include <osi/mouse.hpp>
+#include <osi/window.hpp>
+#include <utils/math.hpp>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+using scene_ptr = std::shared_ptr<Scene>;
+
+std::pair<float, float> intersect_axis(float ray_direction_a, float ray_position_a, float LH_first_a, float LH_second_a);
+std::pair<float, float> intersect(glm::vec3 ray_position, glm::vec3 ray_direction, std::pair<glm::vec3, glm::vec3> LH);
+// findIntersection uses functions 'intersect_axis' and 'intersect' and is NOT node tree compatible yet
+node_ptr find_intersection(const std::vector<node_ptr> &scene, glm::vec3 ray_position, glm::vec3 ray_direction);
+
+std::pair<glm::vec3, glm::vec3> getPickRay(const scene_ptr &scene, const osi::Mouse &mouse, const osi::Window &window);
+
+#endif
\ No newline at end of file
diff --git a/src/algo/src/cursor.cpp b/src/algo/src/cursor.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4eea6f282b6776066decb701d98a799be15a97f9
--- /dev/null
+++ b/src/algo/src/cursor.cpp
@@ -0,0 +1,118 @@
+#include <algo/cursor.hpp>
+#include <gfx/camera.hpp>
+#include <gfx/mesh.hpp>
+#include <gfx/render.hpp>
+
+#include <limits>
+
+std::pair<float, float> intersect_axis(float ray_direction_a, float ray_position_a, 
+                                                  float LH_first_a, float LH_second_a)
+{
+    float t_low;
+    float t_high;
+    if (ray_direction_a == 0 && LH_first_a <= ray_position_a && ray_position_a <= LH_second_a)
+    {
+        t_low = -std::numeric_limits<float>::infinity();
+        t_high = std::numeric_limits<float>::infinity();
+    }
+    else
+    {
+        t_low = glm::min((LH_first_a - ray_position_a) / ray_direction_a, 
+                         (LH_second_a - ray_position_a) / ray_direction_a);
+        t_high = glm::max((LH_first_a - ray_position_a) / ray_direction_a,
+                          (LH_second_a - ray_position_a) / ray_direction_a);
+    }
+
+    return { t_low, t_high };
+}
+
+std::pair<float, float> intersect(glm::vec3 ray_position, glm::vec3 ray_direction, 
+                                             std::pair<glm::vec3, glm::vec3> LH)
+{
+    auto [ tx, tX ] = intersect_axis(ray_direction.x, ray_position.x, LH.first.x, LH.second.x);
+    auto [ ty, tY ] = intersect_axis(ray_direction.y, ray_position.y, LH.first.y, LH.second.y);
+    auto [ tz, tZ ] = intersect_axis(ray_direction.z, ray_position.z, LH.first.z, LH.second.z);
+
+    float t0 = glm::max(tx, glm::max(ty, tz));
+    float t1 = glm::min(tX, glm::min(tY, tZ));
+
+    return {t0, t1};
+}
+
+node_ptr find_intersection(const std::vector<node_ptr> &scene, glm::vec3 ray_position, glm::vec3 ray_direction)
+{
+    node_ptr nearest_node = nullptr;
+    float nearest_t = std::numeric_limits<float>::infinity();
+    for (auto &node : scene)
+    {
+        for (auto &object : node->getObjects())
+        {
+            auto obj = dynamic_pointer_cast<Mesh>(object);
+            if (!obj || !obj->isSelectable())
+                continue;
+            
+            auto LH_world = obj->getAABB().getLHWorld(node);
+            auto [ t0, t1 ] = intersect(ray_position, ray_direction, LH_world);
+
+            if ( t1 < t0 || t1 < 0)
+                continue;
+            if (t0 < 0 && t1 < nearest_t)
+            {
+                nearest_t = t1;
+                nearest_node = node;
+            }
+            else if (t0 < nearest_t)
+            {
+                nearest_t = t0;
+                nearest_node = node;
+            }
+        }
+    }
+
+    return nearest_node;
+}
+
+std::pair<glm::vec3, glm::vec3> getPickRay(const scene_ptr &scene, const osi::Mouse &mouse, const osi::Window &window)
+{
+    camera_ptr camera = scene->getActiveCameras()[0].lock(); // Should be changed to which viewport is in focus
+    glm::mat4 view = get_view_matrix(camera);
+    glm::mat4 projection = get_projection_matrix(camera);
+    
+    // https://gamedev.stackexchange.com/questions/12360/how-do-you-determine-which-object-surface-the-users-pointing-at-with-lwjgl/12370#12370
+    //get the mouse position in screenSpace coords
+    float aspect_ratio = camera->getWindowSize().x / camera->getWindowSize().y;
+    float screen_space_X = ((float) mouse.pos().x / (window.size().x / 2) - 1.0f) * aspect_ratio;
+    float screen_space_Y = (1.0f - (float) mouse.pos().y / (window.size().y / 2));
+
+    float view_ratio = glm::tan((glm::pi<float>() / (180.f/camera->getFOV()) / 2.0f));  // * zoomFactor;
+
+    screen_space_X = screen_space_X * view_ratio;
+    screen_space_Y = screen_space_Y * view_ratio;
+
+    //Find the far and near camera spaces
+    float near_plane = 0.1f;
+    float far_plane = 100.0f;
+    glm::vec4 camera_space_near = glm::vec4(screen_space_X * near_plane, screen_space_Y * near_plane, -near_plane, 1);
+    glm::vec4 camera_space_far = glm::vec4(screen_space_X * far_plane,  screen_space_Y * far_plane,  -far_plane, 1);
+
+    //Unproject the 2D window into 3D to see where in 3D we're actually clicking
+    glm::mat4 inv_view = glm::inverse(view);
+    // Matrix4f.transform(invView, cameraSpaceNear, world_space_near); ??????????????
+    glm::vec4 world_space_near = inv_view * camera_space_near;
+    // Matrix4f.transform(invView, cameraSpaceFar, world_space_far); ???????????????
+    glm::vec4 world_space_far = camera_space_far * world_space_far;
+
+    //calculate the ray position and direction
+    glm::vec3 ray_position = glm::vec3(world_space_near.x, world_space_near.y, world_space_near.z);
+    glm::vec3 ray_direction = -glm::vec3(world_space_far.x - world_space_near.x, 
+                                        world_space_far.y - world_space_near.y, 
+                                        world_space_far.z - world_space_near.z);
+
+    ray_direction = camera->getFrame()->getRotation() * ray_direction;
+
+    /* VISUAL REPRESENTATION OF RAY */
+    // addRay(ray_position, ray_direction);
+
+    ray_direction = glm::normalize(ray_direction);
+
+    return {ray_position, ray_direction};}
\ No newline at end of file
diff --git a/src/controls/CMakeLists.txt b/src/controls/CMakeLists.txt
index 4ff3517e4afec4ea300e1a2be2cb6d1ce0b318e4..14a49763f2d5e12e371ca6e5dad1ab39004874c5 100644
--- a/src/controls/CMakeLists.txt
+++ b/src/controls/CMakeLists.txt
@@ -10,6 +10,8 @@ add_library(${THIS_TARGET_NAME}
     ./include/controls/obj_control.hpp
     ./src/obj_control.cpp
 
+    ./include/controls/control_hub.hpp
+    ./src/control_hub.cpp
     )
 
 set_target_properties(${THIS_TARGET_NAME} PROPERTIES
diff --git a/src/controls/include/controls/control_hub.hpp b/src/controls/include/controls/control_hub.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..d2425616648a7bb18d3df626acde6094c59b302f
--- /dev/null
+++ b/src/controls/include/controls/control_hub.hpp
@@ -0,0 +1,9 @@
+#ifndef CONTROL_HUB_INCLUDED
+#define CONTROL_HUB_INCLUDED
+
+class ControlHub
+{
+
+};
+
+#endif
\ No newline at end of file
diff --git a/src/controls/src/control_hub.cpp b/src/controls/src/control_hub.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d67a4774a34a0febf64f85ca0222994fe96e1540
--- /dev/null
+++ b/src/controls/src/control_hub.cpp
@@ -0,0 +1 @@
+#include <controls/control_hub.hpp>
\ No newline at end of file
diff --git a/src/studio/include/studio/simulator.hpp b/src/studio/include/studio/simulator.hpp
index e513decf389209d38e023467fb63f46f7ac23a75..bf10ee7ac01fa7575d2f93d644bb0d7f2a3a87f3 100644
--- a/src/studio/include/studio/simulator.hpp
+++ b/src/studio/include/studio/simulator.hpp
@@ -43,7 +43,7 @@ struct Simulator : public osi::Simulator
     
     void processInput();
 
-    std::pair<glm::vec3, glm::vec3> getPickRay();
+    // std::pair<glm::vec3, glm::vec3> getPickRay();
 
     void selectObject();
     void rotateObject();
@@ -72,13 +72,6 @@ private:
     /* SHADERS */
     std::map<std::string, shader_ptr> my_shaders;
 };
-
-std::pair<float, float> intersect_axis(float ray_direction_a, float ray_position_a, float LH_first_a, float LH_second_a);
-std::pair<float, float> intersect(glm::vec3 ray_position, glm::vec3 ray_direction, std::pair<glm::vec3, glm::vec3> LH);
-// findIntersection uses functions 'intersect_axis' and 'intersect' and is NOT node tree compatible yet
-// Also unsure if it should be a free function
-node_ptr find_intersection(const std::vector<node_ptr> &scene, glm::vec3 ray_position, glm::vec3 ray_direction);
-
 }
 
 #endif
diff --git a/src/studio/src/simulator.cpp b/src/studio/src/simulator.cpp
index bf71180c2395dcbcc56527f0c910074b84cfc3a6..e5a0a0407e2f768bd5f183f03fc5c95cb39ab4b4 100644
--- a/src/studio/src/simulator.cpp
+++ b/src/studio/src/simulator.cpp
@@ -1,5 +1,6 @@
 #include <studio/simulator.hpp>
 
+#include <algo/cursor.hpp> // TEMPORARY
 #include <filein/obj_loader.hpp>
 #include <gfx/render.hpp>
 #include <gui/ui.hpp>
@@ -92,58 +93,12 @@ void Simulator::processInput()
     ASSUMPTION(glGetError() == GL_NO_ERROR);
 }
 
-std::pair<glm::vec3, glm::vec3> Simulator::getPickRay()
-{
-    camera_ptr camera = scene->getActiveCameras()[0].lock(); // Should be changed to which viewport is in focus
-    glm::mat4 view = get_view_matrix(camera);
-    glm::mat4 projection = get_projection_matrix(camera);
-    
-    // https://gamedev.stackexchange.com/questions/12360/how-do-you-determine-which-object-surface-the-users-pointing-at-with-lwjgl/12370#12370
-    //get the mouse position in screenSpace coords
-    float aspect_ratio = camera->getWindowSize().x / camera->getWindowSize().y;
-    float screen_space_X = ((float) mouse().pos().x / (window().size().x / 2) - 1.0f) * aspect_ratio;
-    float screen_space_Y = (1.0f - (float) mouse().pos().y / (window().size().y / 2));
-
-    float view_ratio = glm::tan((glm::pi<float>() / (180.f/camera->getFOV()) / 2.0f));  // * zoomFactor;
-
-    screen_space_X = screen_space_X * view_ratio;
-    screen_space_Y = screen_space_Y * view_ratio;
-
-    //Find the far and near camera spaces
-    float near_plane = 0.1f;
-    float far_plane = 100.0f;
-    glm::vec4 camera_space_near = glm::vec4(screen_space_X * near_plane, screen_space_Y * near_plane, -near_plane, 1);
-    glm::vec4 camera_space_far = glm::vec4(screen_space_X * far_plane,  screen_space_Y * far_plane,  -far_plane, 1);
-
-    //Unproject the 2D window into 3D to see where in 3D we're actually clicking
-    glm::mat4 inv_view = glm::inverse(view);
-    // Matrix4f.transform(invView, cameraSpaceNear, world_space_near); ??????????????
-    glm::vec4 world_space_near = inv_view * camera_space_near;
-    // Matrix4f.transform(invView, cameraSpaceFar, world_space_far); ???????????????
-    glm::vec4 world_space_far = camera_space_far * world_space_far;
-
-    //calculate the ray position and direction
-    glm::vec3 ray_position = glm::vec3(world_space_near.x, world_space_near.y, world_space_near.z);
-    glm::vec3 ray_direction = -glm::vec3(world_space_far.x - world_space_near.x, 
-                                        world_space_far.y - world_space_near.y, 
-                                        world_space_far.z - world_space_near.z);
-
-    ray_direction = camera->getFrame()->getRotation() * ray_direction;
-
-    /* VISUAL REPRESENTATION OF RAY */
-    // addRay(ray_position, ray_direction);
-
-    ray_direction = glm::normalize(ray_direction);
-
-    return {ray_position, ray_direction};
-}
-
 void Simulator::selectObject()
 {
     if (!mouse().just_released().contains("MouseLeft"))
         return;
 
-    auto [ ray_position, ray_direction ] = getPickRay();
+    auto [ ray_position, ray_direction ] = getPickRay(scene, mouse(), window());
 
     auto nearest_node = find_intersection(scene->getScene(), ray_position, ray_direction);
 
@@ -166,7 +121,7 @@ void Simulator::moveObject()
         return;
     }
 
-    auto [ ray_position, ray_direction ] = getPickRay();
+    auto [ ray_position, ray_direction ] = getPickRay(scene, mouse(), window());
 
     // Initial click not onto the selected object
     if (!object_moving && find_intersection(scene->getScene(), ray_position, ray_direction) != scene->getSelection()) 
@@ -202,7 +157,7 @@ void Simulator::addObject()
     glm::quat rotation = scene->getSelection()->getFrame()->getRotation();
     glm::vec3 scale = scene->getSelection()->getFrame()->getScale();
 
-    auto [ ray_position , ray_direction ] = getPickRay();
+    auto [ ray_position , ray_direction ] = getPickRay(scene, mouse(), window());
 
     float distance = glm::length(ray_position - position);
     
@@ -255,75 +210,6 @@ void Simulator::processMouse()
     }
 }
 
-/* INDEPENDENT FUNCTIONS */
-
-std::pair<float, float> intersect_axis(float ray_direction_a, float ray_position_a, 
-                                                  float LH_first_a, float LH_second_a)
-{
-    float t_low;
-    float t_high;
-    if (ray_direction_a == 0 && LH_first_a <= ray_position_a && ray_position_a <= LH_second_a)
-    {
-        t_low = -std::numeric_limits<float>::infinity();
-        t_high = std::numeric_limits<float>::infinity();
-    }
-    else
-    {
-        t_low = glm::min((LH_first_a - ray_position_a) / ray_direction_a, 
-                         (LH_second_a - ray_position_a) / ray_direction_a);
-        t_high = glm::max((LH_first_a - ray_position_a) / ray_direction_a,
-                          (LH_second_a - ray_position_a) / ray_direction_a);
-    }
-
-    return { t_low, t_high };
-}
-
-std::pair<float, float> intersect(glm::vec3 ray_position, glm::vec3 ray_direction, 
-                                             std::pair<glm::vec3, glm::vec3> LH)
-{
-    auto [ tx, tX ] = intersect_axis(ray_direction.x, ray_position.x, LH.first.x, LH.second.x);
-    auto [ ty, tY ] = intersect_axis(ray_direction.y, ray_position.y, LH.first.y, LH.second.y);
-    auto [ tz, tZ ] = intersect_axis(ray_direction.z, ray_position.z, LH.first.z, LH.second.z);
-
-    float t0 = glm::max(tx, glm::max(ty, tz));
-    float t1 = glm::min(tX, glm::min(tY, tZ));
-
-    return {t0, t1};
-}
-
-node_ptr find_intersection(const std::vector<node_ptr> &scene, glm::vec3 ray_position, glm::vec3 ray_direction)
-{
-    node_ptr nearest_node = nullptr;
-    float nearest_t = std::numeric_limits<float>::infinity();
-    for (auto &node : scene)
-    {
-        for (auto &object : node->getObjects())
-        {
-            auto obj = dynamic_pointer_cast<Mesh>(object);
-            if (!obj || !obj->isSelectable())
-                continue;
-            
-            auto LH_world = obj->getAABB().getLHWorld(node);
-            auto [ t0, t1 ] = intersect(ray_position, ray_direction, LH_world);
-
-            if ( t1 < t0 || t1 < 0)
-                continue;
-            if (t0 < 0 && t1 < nearest_t)
-            {
-                nearest_t = t1;
-                nearest_node = node;
-            }
-            else if (t0 < nearest_t)
-            {
-                nearest_t = t0;
-                nearest_node = node;
-            }
-        }
-    }
-
-    return nearest_node;
-}
-
 /* UNUSED */
 std::vector<float> get_normals(std::vector<unsigned int> &indices, std::vector<float> &vertices)
 {