diff --git a/CMakeLists.txt b/CMakeLists.txt
index 345e2d5070556001ed0f2b7c895ac5f678bb269a..659a7beb69a6629d6949dcecda36091389554c66 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -29,6 +29,7 @@ armarx_find_package(PUBLIC MATHLIB QUIET)
 armarx_find_package(PUBLIC Eigen3 QUIET)
 armarx_find_package(PUBLIC SOEM)
 # armarx_find_package(PUBLIC Simox ${ArmarX_Simox_VERSION} QUIET)
+armarx_find_package(PUBLIC SimoxControl QUIET)
 
 add_subdirectory(source/armarx/control)
 add_subdirectory(examples)
diff --git a/source/armarx/control/CMakeLists.txt b/source/armarx/control/CMakeLists.txt
index 6c4dfbdb4b339daa6a43254f05bff3e0fd6f2221..6a153dd249ec17728d32e4734f1d77a256d8973e 100644
--- a/source/armarx/control/CMakeLists.txt
+++ b/source/armarx/control/CMakeLists.txt
@@ -19,6 +19,7 @@ add_subdirectory(client)
 
 # Skills
 add_subdirectory(skills)
+add_subdirectory(pointing)
 
 # UI.
 add_subdirectory(ui)
@@ -26,4 +27,3 @@ add_subdirectory(ui)
 # Components
 add_subdirectory(retrieve_hand)
 add_subdirectory(components)
-
diff --git a/source/armarx/control/components/control_skill_provider/CMakeLists.txt b/source/armarx/control/components/control_skill_provider/CMakeLists.txt
index 5996bbdb04c7c3867edbe4271af7ee01cafaa3b1..895d107463135be98812dddd17ba9a6ea0c1a365 100644
--- a/source/armarx/control/components/control_skill_provider/CMakeLists.txt
+++ b/source/armarx/control/components/control_skill_provider/CMakeLists.txt
@@ -25,4 +25,5 @@ armarx_add_component(control_skill_provider
         # armarx_control
         armarx_control::skills_constants
         armarx_control::skills
+        armarx_control::pointing_skills
 )
diff --git a/source/armarx/control/components/control_skill_provider/Component.cpp b/source/armarx/control/components/control_skill_provider/Component.cpp
index 870adff151685f83a4229b6f2dee5e635e315fea..7c2f9ceae6e69dc627391313008c42eae058d6a8 100644
--- a/source/armarx/control/components/control_skill_provider/Component.cpp
+++ b/source/armarx/control/components/control_skill_provider/Component.cpp
@@ -2,6 +2,7 @@
 
 #include <ArmarXCore/libraries/DecoupledSingleComponent/Decoupled.h>
 
+#include <armarx/control/pointing/skills/Pointing.h>
 #include <armarx/control/skills/skills/MoveJointsToPosition.h>
 #include <armarx/control/skills/skills/MoveJointsWithVelocity.h>
 #include <armarx/control/skills/skills/RelaxHand.h>
@@ -77,6 +78,11 @@ namespace armarx::control::components::control_skill_provider
                     .robotUnitPlugin_ = remote.robotUnitPlugin};
                 skillBlueprints.zeroTorque->connectTo(context);
             }
+            {
+                armarx::control::pointing::skills::Pointing::Remote remote{
+                    properties.robotName, arviz, memoryNameSystem(), getTrajectoryPlayer()};
+                addSkillFactory<armarx::control::pointing::skills::Pointing>(remote);
+            }
         }
     }
 
diff --git a/source/armarx/control/components/control_skill_provider/Component.h b/source/armarx/control/components/control_skill_provider/Component.h
index cad36113a39024cb8246f54f9958cf1f31f11a29..d4610c2700f89ba93dc0c87924b75f775453e02b 100644
--- a/source/armarx/control/components/control_skill_provider/Component.h
+++ b/source/armarx/control/components/control_skill_provider/Component.h
@@ -4,7 +4,9 @@
 
 #include <RobotAPI/interface/units/HandUnitInterface.h>
 #include <RobotAPI/interface/units/KinematicUnitInterface.h>
+#include <RobotAPI/libraries/RobotAPIComponentPlugins/ArVizComponentPlugin.h>
 #include <RobotAPI/libraries/RobotAPIComponentPlugins/RobotUnitComponentPlugin.h>
+#include <RobotAPI/libraries/RobotAPIComponentPlugins/TrajectoryPlayerComponentPlugin.h>
 #include <RobotAPI/libraries/armem/client/plugins/PluginUser.h>
 #include <RobotAPI/libraries/armem/client/plugins/ReaderWriterPlugin.h>
 #include <RobotAPI/libraries/armem_robot_state/client/common/VirtualRobotReader.h>
@@ -20,7 +22,9 @@ namespace armarx::control::components::control_skill_provider
         virtual public ::armarx::Component,
         virtual public ::armarx::control::components::control_skill_provider::ComponentInterface,
         virtual public ::armarx::SkillProviderComponentPluginUser,
-        virtual public ::armarx::armem::client::PluginUser
+        virtual public ::armarx::armem::client::PluginUser,
+        virtual public ::armarx::ArVizComponentPluginUser,
+        virtual public ::armarx::TrajectoryPlayerComponentPluginUser
     {
 
     public:
diff --git a/source/armarx/control/pointing/CMakeLists.txt b/source/armarx/control/pointing/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..33983f74805a12eb9e23ef3469483724e8418354
--- /dev/null
+++ b/source/armarx/control/pointing/CMakeLists.txt
@@ -0,0 +1,5 @@
+add_subdirectory(constants)
+add_subdirectory(aron)
+add_subdirectory(core)
+add_subdirectory(skill_ids)
+add_subdirectory(skills)
diff --git a/source/armarx/control/pointing/aron/CMakeLists.txt b/source/armarx/control/pointing/aron/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..93f1e1e4dc69d6e0172ede4a66b7b4dc58667fd2
--- /dev/null
+++ b/source/armarx/control/pointing/aron/CMakeLists.txt
@@ -0,0 +1,4 @@
+armarx_add_aron_library(pointing_aron
+    ARON_FILES
+        PointingParams.xml
+)
diff --git a/source/armarx/control/pointing/aron/PointingParams.xml b/source/armarx/control/pointing/aron/PointingParams.xml
new file mode 100644
index 0000000000000000000000000000000000000000..1c791ea242dccbdf0db4ec08ea4bf1d7a4f24526
--- /dev/null
+++ b/source/armarx/control/pointing/aron/PointingParams.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<AronTypeDefinition>
+    <GenerateTypes>
+
+        <IntEnum name="::armarx::control::pointing::arondto::Side">
+            <EnumValue key="Left" value="0"/>
+            <EnumValue key="Right" value="1"/>
+        </IntEnum>
+
+
+        <!-- 'ShapeHand'-skill parameters -->
+        <Object name="::armarx::control::pointing::arondto::PointingParams">
+
+            <!-- Side of the arm to point with -->
+            <ObjectChild key="side">
+                <::armarx::control::pointing::arondto::Side />
+            </ObjectChild>
+
+            <!-- Name of the robot node relative to whose frame 'framedTarget' is given -->
+            <ObjectChild key="frame">
+                <string />
+            </ObjectChild>
+
+            <!-- Position to point at in frame 'frame' -->
+            <ObjectChild key="framedTarget">
+                <position />
+            </ObjectChild>
+
+            <!-- Name of the hand preshape to use. If none is provided, the hand will be
+            ignored. -->
+            <ObjectChild key="handShape">
+                <string optional="true" />
+            </ObjectChild>
+
+            <!-- Maximum velocity -->
+            <ObjectChild key="maxVelocity">
+                <float64/>
+            </ObjectChild>
+
+            <!-- Maximum acceleration -->
+            <ObjectChild key="maxAcceleration">
+                <float64/>
+            </ObjectChild>
+
+        </Object>
+
+    </GenerateTypes>
+</AronTypeDefinition>
diff --git a/source/armarx/control/pointing/constants/CMakeLists.txt b/source/armarx/control/pointing/constants/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..6a21d03876098b56f2154e5c86ce170d0e084e80
--- /dev/null
+++ b/source/armarx/control/pointing/constants/CMakeLists.txt
@@ -0,0 +1,6 @@
+armarx_add_library(pointing_constants
+    SOURCES
+        constants.cpp
+    HEADERS
+        constants.h
+)
diff --git a/source/armarx/control/pointing/constants/constants.cpp b/source/armarx/control/pointing/constants/constants.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..767d21bce99716f000a3e1bfb740af9710739724
--- /dev/null
+++ b/source/armarx/control/pointing/constants/constants.cpp
@@ -0,0 +1,8 @@
+#include "constants.h"
+
+namespace armarx::control::pointing
+{
+    const std::string constants::ARVIZ_LAYER_NAME = "Pointing";
+    const std::string constants::POINTING_SKILL_NAME = "Pointing";
+
+} // namespace armarx::control::pointing
diff --git a/source/armarx/control/pointing/constants/constants.h b/source/armarx/control/pointing/constants/constants.h
new file mode 100644
index 0000000000000000000000000000000000000000..426db639e01e28c2ef7dcde1d24806dd510a2d76
--- /dev/null
+++ b/source/armarx/control/pointing/constants/constants.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include <string>
+
+namespace armarx::control::pointing::constants
+{
+    extern const std::string ARVIZ_LAYER_NAME;
+    extern const std::string POINTING_SKILL_NAME;
+
+} // namespace armarx::control::pointing::constants
diff --git a/source/armarx/control/pointing/core/CMakeLists.txt b/source/armarx/control/pointing/core/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..5a21b793d874d6bcce780bc31be2cde1dd95e359
--- /dev/null
+++ b/source/armarx/control/pointing/core/CMakeLists.txt
@@ -0,0 +1,31 @@
+armarx_add_library(pointing_core
+    SOURCES
+        PointingIK.cpp
+        Pointing.cpp
+
+    HEADERS
+        Side.h
+        PointingIK.h
+        Pointing.h
+
+    DEPENDENCIES_PUBLIC
+        # ArmarXCore
+        ArmarXCore
+
+        #RobotAPI
+        RobotAPICore
+        RobotAPIInterfaces
+        armem
+        armem_robot_state
+        ArViz
+
+        # Simox
+        VirtualRobot
+
+        # SimoxControl
+        RobotAPICore
+        SimoxControl::example_method
+
+        # pointing
+        pointing_constants
+)
diff --git a/source/armarx/control/pointing/core/Pointing.cpp b/source/armarx/control/pointing/core/Pointing.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f10ca43c321c3d6189d5119144088162f7873ebf
--- /dev/null
+++ b/source/armarx/control/pointing/core/Pointing.cpp
@@ -0,0 +1,168 @@
+#include "Pointing.h"
+
+#include <ArmarXCore/core/time.h>
+
+#include <RobotAPI/libraries/armem_robot_state/client/common/VirtualRobotReader.h>
+
+#include <armarx/control/pointing/constants/constants.h>
+
+#include "PointingIK.h"
+
+namespace armarx::control::pointing::core
+{
+    Pointing::Pointing(const Remote& remote) : remote_(remote)
+    {
+    }
+
+    void
+    Pointing::execute(const Pointing::Parameters params)
+    {
+        aborted = false;
+
+        ARMARX_INFO << "Pointing with " << ((params.side == Side::LEFT) ? "left" : "right")
+                    << " arm at " << params.framedTarget << " in frame '" << params.frame << "'"
+                    << (params.handShape ? " with hand shape '" + *params.handShape + "'" : "");
+
+        remote_.trajectoryPlayer->stopTrajectoryPlayer();
+
+        VirtualRobot::RobotPtr robot = getRobot();
+
+        auto target = PositionInGlobalFrame(robot, params.frame, params.framedTarget);
+        visualizeTarget(target);
+
+        armarx::TrajectoryPtr traj = computTrajectory(robot, params.side, target);
+
+        if (params.handShape)
+        {
+            auto shapeJointValues = GetShapeJointValues(robot, params.side, *params.handShape);
+            addHandTrajectory(robot, traj, shapeJointValues);
+        }
+
+        traj = traj->calculateTimeOptimalTrajectory(
+            params.maxVelocity, params.maxAcceleration, 0.0, IceUtil::Time::milliSeconds(10));
+
+        playTrajectory(traj);
+    }
+
+    void
+    Pointing::abort()
+    {
+        aborted = true;
+    }
+
+    VirtualRobot::RobotPtr
+    Pointing::getRobot()
+    {
+        armarx::armem::robot_state::VirtualRobotReader robotReader;
+        robotReader.connect(remote_.memoryNameSystem);
+
+        auto robot = robotReader.getSynchronizedRobot(remote_.robotName);
+        if (not robot)
+        {
+            ARMARX_ERROR << "Could not get synchronized robot '" << remote_.robotName << "'";
+            throw PointingFailedException();
+        }
+        return robot;
+    }
+
+    void
+    Pointing::visualizeTarget(const Eigen::Vector3f& target)
+    {
+        auto layer = remote_.arviz.layer(constants::ARVIZ_LAYER_NAME);
+        layer.add(armarx::viz::Sphere("Pointing target")
+                      .radius(50)
+                      .color(armarx::viz::Color::orange())
+                      .position(target));
+        remote_.arviz.commit(layer);
+    }
+
+    TrajectoryPtr
+    Pointing::computTrajectory(VirtualRobot::RobotPtr robot, Side side, Eigen::Vector3f target)
+    {
+        ARMARX_VERBOSE << "Computing trajectory...";
+        auto ik = PointingIK(robot, side);
+        armarx::TrajectoryPtr traj = ik.computeTrajectory(target);
+        if (not traj)
+        {
+            ARMARX_WARNING << "IK didn't find a solution";
+            throw PointingFailedException();
+        }
+        ARMARX_VERBOSE << "Done computing trajectory";
+
+        if (traj->getLength() == 1) // already pointing at target
+        {
+            return armarx::TrajectoryPtr(new Trajectory);
+        }
+        return traj;
+    }
+
+    void
+    Pointing::addHandTrajectory(VirtualRobot::RobotPtr robot,
+                                armarx::TrajectoryPtr& traj,
+                                const std::map<std::string, float>& shapeJointValues)
+    {
+        for (const auto& [jointName, targetValue] : shapeJointValues)
+        {
+            traj->addDimension({robot->getRobotNode(jointName)->getJointValue(), targetValue},
+                               Ice::DoubleSeq(),
+                               jointName);
+        }
+    }
+
+    void
+    Pointing::playTrajectory(armarx::TrajectoryPtr traj)
+    {
+        ARMARX_INFO << "Playing trajectory (" << traj->getTimeLength() << "s)";
+        remote_.trajectoryPlayer->loadJointTraj(traj);
+        remote_.trajectoryPlayer->startTrajectoryPlayer();
+
+
+        using namespace armarx::core::time;
+        DateTime waitUnil = DateTime::Now() + Duration::Seconds(traj->getTimeLength());
+
+        Clock clock;
+        while (DateTime::Now() < waitUnil)
+        {
+            if (aborted)
+            {
+                ARMARX_INFO << "Pointing aborted. Stopping trajectory";
+                remote_.trajectoryPlayer->stopTrajectoryPlayer();
+                throw PointingAbortedException();
+            }
+            clock.waitFor(Duration::MilliSeconds(10));
+        }
+        ARMARX_INFO << "Pointing target reached";
+    }
+
+    std::map<std::string, float>
+    Pointing::GetShapeJointValues(VirtualRobot::RobotPtr robot,
+                                  Side side,
+                                  const std::string& shapeName)
+    {
+        auto eefName = std::string("Hand_") + (side == Side::LEFT ? "L" : "R") + "_EEF"; // TODO
+        auto eef = robot->getEndEffector(eefName);
+
+        auto shape = eef->getPreshape(shapeName);
+        if (not shape)
+        {
+            ARMARX_ERROR << "Hand '" << eefName << "' has no shape '" << shape << "'";
+            throw PointingFailedException();
+        }
+        return shape->getRobotNodeJointValueMap();
+    }
+
+    Eigen::Vector3f
+    Pointing::PositionInGlobalFrame(VirtualRobot::RobotPtr robot,
+                                    const std::string& frame,
+                                    const Eigen::Vector3f& framedPosition)
+    {
+        auto node = robot->getRobotNode(frame);
+        if (not node)
+        {
+            ARMARX_ERROR << "Robot '" << robot->getName() << "' has no node '" << frame << "'";
+            throw PointingFailedException();
+        }
+        return node->toGlobalCoordinateSystemVec(framedPosition);
+    }
+
+} // namespace armarx::control::pointing::core
diff --git a/source/armarx/control/pointing/core/Pointing.h b/source/armarx/control/pointing/core/Pointing.h
new file mode 100644
index 0000000000000000000000000000000000000000..e3ef764b9a08f97ca805475b22d68e5c771a8599
--- /dev/null
+++ b/source/armarx/control/pointing/core/Pointing.h
@@ -0,0 +1,117 @@
+#pragma once
+
+#include <VirtualRobot/VirtualRobot.h>
+
+#include <RobotAPI/components/ArViz/Client/Client.h>
+#include <RobotAPI/interface/components/TrajectoryPlayerInterface.h>
+#include <RobotAPI/libraries/armem/client/MemoryNameSystem.h>
+#include <RobotAPI/libraries/core/Trajectory.h>
+
+#include "Side.h"
+
+namespace armarx::control::pointing::core
+{
+
+    /**
+     * @brief Implementation of a 'Pointing'-gesture.
+     *
+     * Lets the robot point somewhere by aligning the forearm with the passed target.
+     *
+     * @ingroup Library-pointing
+     */
+    class Pointing
+    {
+    public:
+        struct Remote
+        {
+            std::string robotName;
+            armarx::viz::Client arviz;
+            armarx::armem::client::MemoryNameSystem memoryNameSystem;
+            armarx::TrajectoryPlayerInterfacePrx trajectoryPlayer;
+        };
+
+        struct Parameters
+        {
+            /** Side of the arm to point with. */
+            Side side;
+
+            /** Name of the robot node relative to whose frame @ref framedTarget is given. */
+            std::string frame;
+
+            /** Position to point at in frame @ref frame. */
+            Eigen::Vector3f framedTarget;
+
+            /** Name of the hand preshape to use. If none is provided, the hand will be ignored. */
+            std::optional<std::string> handShape;
+
+            /** Maximum velocity */
+            double maxVelocity;
+
+            /** Maximum acceleration */
+            double maxAcceleration;
+        };
+
+        class PointingException : public std::exception
+        {
+        };
+
+        class PointingFailedException : public PointingException
+        {
+        };
+
+        class PointingAbortedException : public PointingException
+        {
+        };
+
+
+    public:
+        Pointing(const Remote& remote);
+
+        /**
+         * @brief Executes the pointing gesture.
+         *
+         * Let's the robot point at the passed target. Blocks unitl the target configuration is
+         * reached or the execution is aborted with @ref abort.
+         *
+         * @param params Parameterization of the pointing gesture.
+         *
+         * @throws PointingFailedException Thrown if the pointing gesture could not be executed.
+         * @throws Thrown when the execution is aborted with @ref abort before reaching the target
+         *         configuration.
+         */
+        void execute(const Parameters params);
+
+        /**
+         * @brief Aborts a running execution.
+         */
+        void abort();
+
+
+    private:
+        VirtualRobot::RobotPtr getRobot();
+
+        void visualizeTarget(const Eigen::Vector3f& target);
+
+        armarx::TrajectoryPtr
+        computTrajectory(VirtualRobot::RobotPtr robot, Side side, Eigen::Vector3f target);
+
+        void addHandTrajectory(VirtualRobot::RobotPtr robot,
+                               armarx::TrajectoryPtr& traj,
+                               const std::map<std::string, float>& shapeJointValues);
+
+        void playTrajectory(armarx::TrajectoryPtr traj);
+
+        static Eigen::Vector3f PositionInGlobalFrame(VirtualRobot::RobotPtr robot,
+                                                     const std::string& frame,
+                                                     const Eigen::Vector3f& framedTarget);
+
+        static std::map<std::string, float>
+        GetShapeJointValues(VirtualRobot::RobotPtr robot, Side side, const std::string& shapeName);
+
+
+    private:
+        Remote remote_;
+        std::atomic_bool aborted;
+    };
+
+} // namespace armarx::control::pointing::core
diff --git a/source/armarx/control/pointing/core/PointingIK.cpp b/source/armarx/control/pointing/core/PointingIK.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..043f0c3f541cc411b4422538adf807d2520bb591
--- /dev/null
+++ b/source/armarx/control/pointing/core/PointingIK.cpp
@@ -0,0 +1,71 @@
+#include "PointingIK.h"
+
+#include <VirtualRobot/Robot.h>
+#include <VirtualRobot/RobotNodeSet.h>
+
+namespace armarx::control::pointing::core
+{
+
+    PointingIK::PointingIK(VirtualRobot::RobotPtr robot, Side side)
+    {
+        auto humanMapping = robot->getHumanMapping();
+        ARMARX_CHECK(humanMapping) << "Robot '" << robot->getName() << "' has no human mapping";
+
+        auto armDescription = (side == LEFT) ? humanMapping->leftArm : humanMapping->rightArm;
+        auto elbow = armDescription.segments.elbow.nodeName;
+        auto wrist = armDescription.segments.wrist.nodeName;
+        auto palm = armDescription.segments.palm.nodeName;
+        auto tcp = armDescription.segments.tcp.nodeName;
+
+        // actuated joints = all arm joints until (excluding) wrist
+        auto nodeSetArm = robot->getRobotNodeSet(armDescription.nodeSet);
+        auto actuatedJoints = nodeSetArm->getNodeNames();
+        actuatedJoints.resize(nodeSetArm->getRobotNodeIndex(wrist));
+
+        // reduced robot must contain shoulder, elbow, wrist and palm for IK to work
+        reducedRobot_ = std::make_unique<simox::control::simox::robot::Robot>(
+            robot, actuatedJoints, std::vector({wrist, palm, tcp}), true);
+
+        // use elbow - tcp (instead of wrist) as forearm to line up with the target
+        ik_ = std::make_unique<simox::control::method::example::PointingIK>(
+            *reducedRobot_,
+            *reducedRobot_->getNode(tcp),
+            *reducedRobot_->getNode(elbow),
+            side == LEFT ? reducedRobot_->humanMapping()->leftArm
+                         : reducedRobot_->humanMapping()->rightArm);
+    }
+
+    TrajectoryPtr
+    PointingIK::computeTrajectory(Eigen::Vector3f& target)
+    {
+        ik_->setPointingTarget(target.cast<double>() / 1000); // simox uses meters
+
+        // actuated joints x timesteps
+        std::vector<std::vector<double>> configs(ik_->getNodeNames().size());
+        auto appendConfig = [&configs](Eigen::VectorXd config)
+        {
+            for (size_t i = 0; i < configs.size(); i++)
+            {
+                configs[i].push_back(config[i]);
+            }
+        };
+
+        appendConfig(ik_->getJointValues());
+
+        Eigen::VectorXd prevJointValues = ik_->getJointValues();
+        while (not ik_->isTargetReached())
+        {
+            ik_->optimize();
+            if ((ik_->getJointValues() - prevJointValues).norm() < 1e-6)
+            {
+                return nullptr;
+            }
+
+            appendConfig(ik_->getJointValues());
+            prevJointValues = ik_->getJointValues();
+        }
+
+        return new Trajectory(configs, Ice::DoubleSeq(), ik_->getNodeNames());
+    }
+
+} // namespace armarx::control::pointing::core
diff --git a/source/armarx/control/pointing/core/PointingIK.h b/source/armarx/control/pointing/core/PointingIK.h
new file mode 100644
index 0000000000000000000000000000000000000000..028e5b4b97dcf70250b25b1b6ec59f27307a7647
--- /dev/null
+++ b/source/armarx/control/pointing/core/PointingIK.h
@@ -0,0 +1,59 @@
+#pragma once
+
+#include <RobotAPI/libraries/core/Trajectory.h>
+
+#include "Side.h"
+#include <simox/control/impl/simox/robot/Robot.h>
+#include <simox/example/method/PointingIK.h>
+
+namespace armarx::control::pointing::core
+{
+
+    /**
+     * @brief IK to compute joint configurations for a pointing gesture.
+     *
+     * Wrapper for simox-control's method::example::PointingIK. Computes pointing gesture joint
+     * configurations that line up the forearm with the target. Uses the robot's human mapping to
+     * obtain relevant joints: All arm-joints until (exluding) the wrist are beeing used.
+     *
+     * @ingroup Library-pointing
+     */
+    class PointingIK
+    {
+    public:
+        /**
+         * @brief Initialize a new PointingIK.
+         * @param robot Robot model with current robot configuration to be used by the IK. Must
+         *        provide a human mapping.
+         * @param side Side of the arm to point with.
+         */
+        PointingIK(VirtualRobot::RobotPtr robot, Side side);
+
+
+        /**
+         * @brief Compute a joint trajectory ending in a configuration in which the robot points in
+         *        the direction of the passed target position.
+         *
+         * The initial timestep of the trajectory contains the current joint configuration of the
+         * robot passed to the constructor. The following timesteps contain the step-by-step
+         * optimized configurations until the end configuration is reached. The trajectory is
+         * constructed without timestamps, i.e., has a duration of 1s with equidistant timesteps.
+         *
+         * Neither the generated trajectory nor the end configuration are guaranteed to be self-
+         * collision-free. Furthermore, generated trajectories aren't efficient in reaching the end
+         * configuration.
+         *
+         * This method may only be called once to ensure a well-defined initial robot configuration.
+         *
+         * @param target Target position to point at in the global coordinate system (mm).
+         * @return The computed trajectory. Null if no solution was found. If the robot has already
+         *         pointed at the target, the trajectory only contains the initial timestep.
+         */
+        TrajectoryPtr computeTrajectory(Eigen::Vector3f& target);
+
+    private:
+        std::unique_ptr<simox::control::simox::robot::Robot> reducedRobot_;
+        std::unique_ptr<simox::control::method::example::PointingIK> ik_;
+    };
+
+} // namespace armarx::control::pointing::core
diff --git a/source/armarx/control/pointing/core/Side.h b/source/armarx/control/pointing/core/Side.h
new file mode 100644
index 0000000000000000000000000000000000000000..e4d2d1a76570d26a74199be238d13218a214554d
--- /dev/null
+++ b/source/armarx/control/pointing/core/Side.h
@@ -0,0 +1,16 @@
+#pragma once
+
+namespace armarx::control::pointing::core
+{
+    /**
+     * @brief Describes the side of an arm.
+     *
+     * @ingroup Library-pointing
+     */
+    enum Side
+    {
+        LEFT,
+        RIGHT
+    };
+
+} // namespace armarx::control::pointing::core
diff --git a/source/armarx/control/pointing/pointing.cpp b/source/armarx/control/pointing/pointing.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a7cb04b733200f58beb3fb92cf12481169280504
--- /dev/null
+++ b/source/armarx/control/pointing/pointing.cpp
@@ -0,0 +1,5 @@
+#include "pointing.h"
+
+namespace armarx::control::pointing
+{
+}
diff --git a/source/armarx/control/pointing/pointing.h b/source/armarx/control/pointing/pointing.h
new file mode 100644
index 0000000000000000000000000000000000000000..8d5a2173fd1e0e14cdc1eef47e05aaafb333302d
--- /dev/null
+++ b/source/armarx/control/pointing/pointing.h
@@ -0,0 +1,11 @@
+#pragma once
+
+namespace armarx::control::pointing
+{
+    /**
+    * @defgroup Library-pointing pointing
+    * @ingroup control
+    *
+    * Let the robot point somewhere.
+    */
+}
diff --git a/source/armarx/control/pointing/skill_ids/CMakeLists.txt b/source/armarx/control/pointing/skill_ids/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2cab8c40a65b8fffa95b85b57df4d218c2e9362a
--- /dev/null
+++ b/source/armarx/control/pointing/skill_ids/CMakeLists.txt
@@ -0,0 +1,14 @@
+armarx_add_library(pointing_skill_ids
+    SOURCES
+        skill_ids.cpp
+
+    HEADERS
+        skill_ids.h
+
+    DEPENDENCIES_PUBLIC
+        # RobotAPI
+        RobotAPISkills
+
+        #pointing
+        armarx_control::pointing_constants
+)
diff --git a/source/armarx/control/pointing/skill_ids/skill_ids.cpp b/source/armarx/control/pointing/skill_ids/skill_ids.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..427b208d45eacaca1330cea7d5f180402c119848
--- /dev/null
+++ b/source/armarx/control/pointing/skill_ids/skill_ids.cpp
@@ -0,0 +1,9 @@
+#include "skill_ids.h"
+
+#include <armarx/control/pointing/constants/constants.h>
+
+namespace armarx::control::pointing::skill_ids
+{
+    const armarx::skills::SkillID Pointing{.skillName = constants::POINTING_SKILL_NAME};
+
+} // namespace armarx::control::pointing::skill_ids
diff --git a/source/armarx/control/pointing/skill_ids/skill_ids.h b/source/armarx/control/pointing/skill_ids/skill_ids.h
new file mode 100644
index 0000000000000000000000000000000000000000..a0a60170f0b8d4c9acc3090a88502692bcf3b622
--- /dev/null
+++ b/source/armarx/control/pointing/skill_ids/skill_ids.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include <RobotAPI/libraries/skills/core/SkillID.h>
+
+namespace armarx::control::pointing::skill_ids
+{
+    extern const armarx::skills::SkillID Pointing;
+
+} // namespace armarx::control::pointing::skill_ids
diff --git a/source/armarx/control/pointing/skills/CMakeLists.txt b/source/armarx/control/pointing/skills/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a8b04636300bf9d799036177480c4ac630528d06
--- /dev/null
+++ b/source/armarx/control/pointing/skills/CMakeLists.txt
@@ -0,0 +1,22 @@
+armarx_add_library(pointing_skills
+    SOURCES
+        Pointing.cpp
+        aron_conversions.cpp
+
+    HEADERS
+        Pointing.h
+        aron_conversions.h
+
+    DEPENDENCIES_PUBLIC
+        # ArmarXCore
+        ArmarXCore
+
+        # RobotAPI
+        RobotAPISkills
+
+        # pointing
+        armarx_control::pointing_constants
+        armarx_control::pointing_aron
+        armarx_control::pointing_core
+        armarx_control::pointing_skill_ids
+)
diff --git a/source/armarx/control/pointing/skills/Pointing.cpp b/source/armarx/control/pointing/skills/Pointing.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c222c742f3af8e2044c0dcf13b22c9edfbe4a0e8
--- /dev/null
+++ b/source/armarx/control/pointing/skills/Pointing.cpp
@@ -0,0 +1,63 @@
+#include "Pointing.h"
+
+#include <armarx/control/pointing/skill_ids/skill_ids.h>
+
+#include "aron_conversions.h"
+
+namespace armarx::control::pointing::skills
+{
+
+    armarx::skills::SkillDescription
+    Pointing::GetSkillDescription()
+    {
+        ParamType defaultParams;
+        defaultParams.side = core::Side::RIGHT;
+        defaultParams.frame = "root";
+        defaultParams.handShape = std::make_optional("Pointing");
+        defaultParams.maxVelocity = 0.4;
+        defaultParams.maxAcceleration = 0.3;
+
+        return armarx::skills::SkillDescription{.skillId = skill_ids::Pointing,
+                                                .description = "Lets the robot point at something.",
+                                                .rootProfileDefaults = defaultParams.toAron(),
+                                                .timeout = armarx::core::time::Duration::Minutes(1),
+                                                .parametersType = ParamType::ToAronType()};
+    }
+
+    Pointing::Pointing(const Remote& remote) : Base(GetSkillDescription()), remote_(remote)
+    {
+    }
+
+    Pointing::MainResult
+    Pointing::main(const SpecializedMainInput& in)
+    {
+        core::Pointing::Parameters parameters = fromAron(in.parameters);
+        impl_.emplace(remote_);
+        try
+        {
+            impl_->execute(parameters);
+        }
+        catch (core::Pointing::PointingFailedException&)
+        {
+            return MakeFailedResult();
+        }
+        catch (core::Pointing::PointingAbortedException&)
+        {
+            return MakeAbortedResult();
+        }
+        return MakeSucceededResult();
+    }
+
+    void
+    Pointing::onStopRequested()
+    {
+        impl_->abort();
+    }
+
+    void
+    Pointing::onTimeoutReached()
+    {
+        impl_->abort();
+    }
+
+} // namespace armarx::control::pointing::skills
diff --git a/source/armarx/control/pointing/skills/Pointing.h b/source/armarx/control/pointing/skills/Pointing.h
new file mode 100644
index 0000000000000000000000000000000000000000..f2374e357a54c97868d918ce0a187b49ae4190e1
--- /dev/null
+++ b/source/armarx/control/pointing/skills/Pointing.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include <RobotAPI/libraries/skills/provider/SimpleSpecializedSkill.h>
+
+#include <armarx/control/pointing/aron/PointingParams.aron.generated.h>
+#include <armarx/control/pointing/core/Pointing.h>
+
+namespace armarx::control::pointing::skills
+{
+    class Pointing : public armarx::skills::SimpleSpecializedSkill<arondto::PointingParams>
+    {
+    public:
+        using Base = armarx::skills::SimpleSpecializedSkill<arondto::PointingParams>;
+
+        static armarx::skills::SkillDescription GetSkillDescription();
+
+        using Remote = core::Pointing::Remote;
+        Pointing(const Remote&);
+
+        MainResult main(const SpecializedMainInput& in) override;
+
+        void onStopRequested() override;
+        void onTimeoutReached() override;
+
+    private:
+        Remote remote_;
+        std::optional<core::Pointing> impl_;
+    };
+
+} // namespace armarx::control::pointing::skills
diff --git a/source/armarx/control/pointing/skills/aron_conversions.cpp b/source/armarx/control/pointing/skills/aron_conversions.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d9e47d78f76c3427479aca4c3151ad4b42072137
--- /dev/null
+++ b/source/armarx/control/pointing/skills/aron_conversions.cpp
@@ -0,0 +1,40 @@
+#include "aron_conversions.h"
+
+namespace armarx::control::pointing
+{
+    void
+    skills::fromAron(const arondto::Side& dto, core::Side& bo)
+    {
+        switch (dto.value)
+        {
+            case arondto::Side::Left:
+                bo = core::Side::LEFT;
+                break;
+            case arondto::Side::Right:
+                bo = core::Side::RIGHT;
+                break;
+            default:
+                ARMARX_ERROR << "Invalid side";
+        }
+    }
+
+    void
+    skills::fromAron(const arondto::PointingParams& dto, core::Pointing::Parameters& bo)
+    {
+        fromAron(dto.side, bo.side);
+        bo.frame = dto.frame;
+        bo.framedTarget = dto.framedTarget;
+        bo.handShape = dto.handShape;
+        bo.maxVelocity = dto.maxVelocity;
+        bo.maxAcceleration = dto.maxAcceleration;
+    }
+
+    core::Pointing::Parameters
+    skills::fromAron(const arondto::PointingParams& dto)
+    {
+        core::Pointing::Parameters bo;
+        fromAron(dto, bo);
+        return bo;
+    }
+
+} // namespace armarx::control::pointing
diff --git a/source/armarx/control/pointing/skills/aron_conversions.h b/source/armarx/control/pointing/skills/aron_conversions.h
new file mode 100644
index 0000000000000000000000000000000000000000..95b98e7ec33eb4f2b80366b51926f4400e0ec991
--- /dev/null
+++ b/source/armarx/control/pointing/skills/aron_conversions.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#include <armarx/control/pointing/aron/PointingParams.aron.generated.h>
+#include <armarx/control/pointing/core/Pointing.h>
+#include <armarx/control/pointing/core/Side.h>
+
+namespace armarx::control::pointing::skills
+{
+    void fromAron(const arondto::Side& dto, core::Side& bo);
+
+    void fromAron(const arondto::PointingParams& dto, core::Pointing::Parameters& bo);
+    core::Pointing::Parameters fromAron(const arondto::PointingParams& dto);
+
+} // namespace armarx::control::pointing::skills