diff --git a/CMakeLists.txt b/CMakeLists.txt
index 722cd6dc1732c94f2d6ae15988245360a1c25c96..defacb53f523dd0c01a82a694386fbe1e972d0ab 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -91,6 +91,7 @@ SET (${PROJECT_NAME}_HEADERS
 
   include/hpp/manipulation/path-optimization/small-steps.hh
   include/hpp/manipulation/path-optimization/config-optimization.hh
+  include/hpp/manipulation/path-optimization/keypoints.hh
   )
 
 ADD_SUBDIRECTORY(src)
diff --git a/include/hpp/manipulation/fwd.hh b/include/hpp/manipulation/fwd.hh
index 075a4011e34313c2a5c42034f413d86f1897a3b6..6a5d3ff7ae3fa288aa38c03347430eefbc1d317a 100644
--- a/include/hpp/manipulation/fwd.hh
+++ b/include/hpp/manipulation/fwd.hh
@@ -135,6 +135,8 @@ namespace hpp {
     namespace pathOptimization {
       HPP_PREDEF_CLASS (SmallSteps);
       typedef boost::shared_ptr < SmallSteps > SmallStepsPtr_t;
+      HPP_PREDEF_CLASS (Keypoints);
+      typedef boost::shared_ptr < Keypoints > KeypointsPtr_t;
     } // namespace pathOptimization
   } // namespace manipulation
 } // namespace hpp
diff --git a/include/hpp/manipulation/path-optimization/keypoints.hh b/include/hpp/manipulation/path-optimization/keypoints.hh
new file mode 100644
index 0000000000000000000000000000000000000000..3bb13f5cb274a0f9f470f54d89253c65f8b2f348
--- /dev/null
+++ b/include/hpp/manipulation/path-optimization/keypoints.hh
@@ -0,0 +1,78 @@
+// Copyright (c) 2016, Joseph Mirabel
+// Authors: Joseph Mirabel (joseph.mirabel@laas.fr)
+//
+// This file is part of hpp-manipulation.
+// hpp-manipulation is free software: you can redistribute it
+// and/or modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation, either version
+// 3 of the License, or (at your option) any later version.
+//
+// hpp-manipulation is distributed in the hope that it will be
+// useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Lesser Public License for more details.  You should have
+// received a copy of the GNU Lesser General Public License along with
+// hpp-manipulation. If not, see <http://www.gnu.org/licenses/>.
+
+#ifndef HPP_MANIPULATION_PATHOPTIMIZATION_KEYPOINTS_HH
+#define HPP_MANIPULATION_PATHOPTIMIZATION_KEYPOINTS_HH
+
+# include <hpp/core/path-optimizer.hh>
+# include <hpp/core/path-optimizer.hh>
+
+# include <hpp/manipulation/fwd.hh>
+# include <hpp/manipulation/config.hh>
+# include <hpp/manipulation/problem.hh>
+# include <hpp/manipulation/graph/fwd.hh>
+
+namespace hpp {
+  namespace manipulation {
+    namespace pathOptimization {
+      using hpp::core::Path;
+      using hpp::core::PathPtr_t;
+      using hpp::core::PathVector;
+      using hpp::core::PathVectorPtr_t;
+      /// \addtogroup path_optimization
+      /// \{
+
+      class HPP_MANIPULATION_DLLAPI Keypoints : public core::PathOptimizer
+      {
+        public:
+          static KeypointsPtr_t create (const core::Problem& problem) {
+            const Problem& p = dynamic_cast <const Problem&> (problem);
+            return KeypointsPtr_t (new Keypoints (p));
+          }
+
+          virtual PathVectorPtr_t optimize (const PathVectorPtr_t& path);
+
+        protected:
+          /// Constructor
+          Keypoints (const Problem& problem) :
+            PathOptimizer (problem), problem_ (problem) {}
+
+        private:
+          const Problem& problem_;
+          struct InterKeypointPath {
+            PathVectorPtr_t path;
+            bool isShort;
+            graph::EdgePtr_t edge;
+          };
+          typedef std::vector<InterKeypointPath> IKPvector_t;
+
+          IKPvector_t split (PathVectorPtr_t path) const;
+
+          PathVectorPtr_t shorten (const IKPvector_t& paths,
+              std::size_t i1, std::size_t i2) const;
+
+          IKPvector_t replaceInPath(const IKPvector_t& input,
+              const PathVectorPtr_t& shortcut,
+              std::size_t i1, std::size_t i2) const;
+      };
+
+      /// \}
+
+    } // namespace pathOptimization
+  } // namespace manipulation
+} // namespace hpp
+
+#endif // HPP_MANIPULATION_PATHOPTIMIZATION_KEYPOINTS_HH
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 81ba479814aa83ce10c1578a34796491af753f30..98ea6c21ddfccfeab44ef184f608176eb32bfe93 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -49,6 +49,7 @@ SET(SOURCES
   graph/dot.cc
 
   path-optimization/config-optimization.cc
+  path-optimization/keypoints.cc
   )
 
 IF(HPP_MANIPULATION_HAS_WHOLEBODY_STEP)
diff --git a/src/path-optimization/keypoints.cc b/src/path-optimization/keypoints.cc
new file mode 100644
index 0000000000000000000000000000000000000000..f0b167136808a6d42b5fab82f2a7717a697b60fd
--- /dev/null
+++ b/src/path-optimization/keypoints.cc
@@ -0,0 +1,211 @@
+// Copyright (c) 2016, Joseph Mirabel
+// Authors: Joseph Mirabel (joseph.mirabel@laas.fr)
+//
+// This file is part of hpp-manipulation.
+// hpp-manipulation is free software: you can redistribute it
+// and/or modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation, either version
+// 3 of the License, or (at your option) any later version.
+//
+// hpp-manipulation is distributed in the hope that it will be
+// useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Lesser Public License for more details.  You should have
+// received a copy of the GNU Lesser General Public License along with
+// hpp-manipulation. If not, see <http://www.gnu.org/licenses/>.
+
+#include <hpp/manipulation/path-optimization/keypoints.hh>
+
+#include <hpp/core/path.hh>
+#include <hpp/core/path-vector.hh>
+#include <hpp/core/path-projector.hh>
+#include <hpp/core/path-validation.hh>
+
+#include <hpp/manipulation/graph/edge.hh>
+#include <hpp/manipulation/graph/graph.hh>
+#include <hpp/manipulation/constraint-set.hh>
+#include <hpp/manipulation/graph-steering-method.hh>
+
+namespace hpp {
+  namespace manipulation {
+    namespace pathOptimization {
+      using std::make_pair;
+
+      PathVectorPtr_t Keypoints::optimize (const PathVectorPtr_t& path)
+      {
+        PathVectorPtr_t input = PathVector::create (
+              path->outputSize(), path->outputDerivativeSize()); 
+        PathVectorPtr_t output = path;
+        path->flatten (input);
+        // Step 1: find the key waypoints
+        // paths is a vector of 
+        IKPvector_t paths = split (input);
+
+        // Step 2: for each couple of consecutive path between keywaypoints,
+        //         try to find a better intermediate key waypoint.
+        std::size_t i1 = 0;
+        bool success = false;
+        for (std::size_t i2 = 1; i2 < paths.size(); ++i2) {
+          if (paths[i2].isShort) continue; // So that i2 = i1 + 2
+          // Try to generate a new key waypoint
+          PathVectorPtr_t shortcut = shorten (paths, i1, i2);
+          if (shortcut) {
+            paths = replaceInPath (paths, shortcut, i1, i2);
+            success = true;
+            while (i2 < paths.size() && !paths[i2].isShort) ++i2;
+          }
+          i1 = i2;
+        }
+
+        if (success) {
+          // Build path
+          PathVectorPtr_t output = PathVector::create (path->outputSize (),
+              path->outputDerivativeSize ());
+          for (std::size_t j = 0; j < paths.size(); ++j)
+            output->concatenate (*paths[j].path);
+        }
+        return output;
+      }
+
+      Keypoints::IKPvector_t Keypoints::split (PathVectorPtr_t input) const
+      {
+        IKPvector_t paths;
+
+        ConstraintSetPtr_t c;
+        for (std::size_t i_s = 0; i_s < input->numberPaths ();) {
+          InterKeypointPath ikpp;
+          ikpp.path = PathVector::create (
+              input->outputSize(), input->outputDerivativeSize()); 
+          PathPtr_t current = input->pathAtRank (i_s);
+          ikpp.path->appendPath (current);
+          c = HPP_DYNAMIC_PTR_CAST (ConstraintSet, current->constraints ());
+          if (c) ikpp.edge = c->edge ();
+          ikpp.isShort = ikpp.edge && ikpp.edge->isShort();
+          std::size_t i_e = i_s + 1;
+          for (; i_e < input->numberPaths (); ++i_e) {
+            current = input->pathAtRank (i_e);
+            c = HPP_DYNAMIC_PTR_CAST (ConstraintSet, current->constraints ());
+            if (!c && ikpp.edge) {
+              hppDout(info, "No manipulation::ConstraintSet");
+              break;
+            }
+            if (c && ikpp.edge->node() != c->edge ()->node()) break;
+            if (ikpp.isShort != c->edge()->isShort()) // We do not optimize edges marked as short
+              break;
+            ikpp.path->appendPath (current);
+          }
+          hppDout(info, "Edge name: " << ikpp.edge->name());
+          i_s = i_e;
+          paths.push_back (ikpp);
+        }
+        return paths;
+      }
+
+      PathVectorPtr_t Keypoints::shorten (const IKPvector_t& paths,
+          std::size_t i1, std::size_t i2) const
+      {
+        PathVectorPtr_t result;
+        const std::size_t maxTrials = 10;
+        value_type cur_length = 0;
+        for (std::size_t i = i1; i < i2 + 1; ++i)
+          cur_length += paths[i].path->length();
+
+        const core::SteeringMethodPtr_t& sm (problem_.steeringMethod ());
+        PathProjectorPtr_t pathProjector (problem().pathProjector ());
+        core::PathValidationPtr_t pathValidation (problem ().pathValidation ());
+
+        // Get all the possible edges of the graph.
+        const InterKeypointPath& ikpp1 = paths[i1];
+        const InterKeypointPath& ikpp2 = paths[i2];
+        graph::Edges_t edges;
+        graph::Graph& graph = *problem_.constraintGraph ();
+        try {
+          edges = graph.getEdges (ikpp1.edge->node(), ikpp2.edge->node());
+        } catch (const std::logic_error& e) {
+          hppDout (error, e.what ());
+          return PathVectorPtr_t ();
+        }
+
+        value_type t0, t1;
+        Configuration_t q  (ikpp1.path->outputSize()),
+                        q1 (ikpp1.path->outputSize()),
+                        q4 (ikpp1.path->outputSize());
+
+        // Unused variables
+        PathValidationReportPtr_t report;
+        PathPtr_t unusedP;
+
+        for (std::size_t n1 = 0; n1 < maxTrials; ++n1) {
+          // Generate a random parameter on p1.
+          t0 = ikpp1.path->timeRange().first;
+          t1 = t0 + ikpp1.path->timeRange().second * rand ()/RAND_MAX; 
+          // Get the configuration and project it.
+          if (!(*ikpp1.path) (q1, t1)) continue;
+          for (graph::Edges_t::const_iterator _edges = edges.begin();
+              _edges != edges.end(); ++_edges) {
+            // TODO: For level set edges, something different should be done.
+            // Maybe, we must add a virtual generateInIntersection (qfrom, qto, qrand)
+            // that would be specialized for each edge.
+            q = q1;
+            if ((*_edges)->applyConstraints (q1,q)) {
+              PathPtr_t short1, proj1;
+              if ((*_edges)->build (short1, q1, q)) {
+                if (!short1 || !pathProjector->apply(short1, proj1)) continue;
+                if (!pathValidation->validate (proj1, false, unusedP, report)) continue;
+
+                // short1 is a valid path leading to the target node.
+                // Try to connect this to ikpp2.path
+                for (std::size_t n2 = 0; n2 < maxTrials; ++n2) {
+                  value_type t3, t4, t5;
+                  t3 = ikpp2.path->timeRange().first;
+                  t4 = t3 + ikpp2.path->timeRange().second * rand ()/RAND_MAX; 
+                  t5 = t3 + ikpp2.path->timeRange().second;
+                  if (!(*ikpp2.path) (q4, t4)) continue;
+                  PathPtr_t short2, proj2;
+                  short2 = (*sm)(q, q4);
+                  if (!short2 || !pathProjector->apply(short2, proj2)) continue;
+                  if (!pathValidation->validate (proj2, false, unusedP, report)) continue;
+                  // short2 is possible. Build the path and check it is shorter.
+
+                  PathVectorPtr_t tmp = PathVector::create (ikpp1.path->outputSize (),
+                      ikpp1.path->outputDerivativeSize ());
+                  tmp->concatenate (*(ikpp1.path->extract
+                        (make_pair (t0, t1))-> as <PathVector> ()));
+                  tmp->appendPath (short1);
+                  tmp->appendPath (short2);
+                  tmp->concatenate (*(ikpp2.path->extract
+                        (make_pair (t4, t5))-> as <PathVector> ()));
+
+                  if (cur_length >= tmp->length()) {
+                    cur_length = tmp->length();
+                    result = tmp;
+                  }
+                }
+              }
+            }
+          }
+        }
+        return result;
+      }
+
+      Keypoints::IKPvector_t Keypoints::replaceInPath(const IKPvector_t& input,
+          const PathVectorPtr_t& shortcut,
+          std::size_t i1, std::size_t i2) const
+      {
+        PathVectorPtr_t flat = PathVector::create (shortcut->outputSize (),
+            shortcut->outputDerivativeSize ());
+        shortcut->flatten(flat);
+
+        PathVectorPtr_t output = PathVector::create (shortcut->outputSize (),
+            shortcut->outputDerivativeSize ());
+        for (std::size_t j = 0; j < i1; ++j)
+          output->concatenate (*input[j].path);
+        output->concatenate (*flat);
+        for (std::size_t j = i2 + 1; j < input.size(); ++j)
+          output->concatenate (*input[j].path);
+
+        return split (output);
+      }
+    } // namespace pathOptimization
+  } // namespace manipulation
+} // namespace hpp
diff --git a/src/problem-solver.cc b/src/problem-solver.cc
index cc3a711b52b9fa5160390b37ffd54349993deaf4..cd90779e0c74449d58164fbd4044bda7b65a2c98 100644
--- a/src/problem-solver.cc
+++ b/src/problem-solver.cc
@@ -47,6 +47,7 @@
 #include "hpp/manipulation/graph-node-optimizer.hh"
 #include "hpp/manipulation/graph-steering-method.hh"
 #include "hpp/manipulation/path-optimization/config-optimization.hh"
+#include "hpp/manipulation/path-optimization/keypoints.hh"
 
 #if HPP_MANIPULATION_HAS_WHOLEBODY_STEP
 #include <hpp/wholebody-step/small-steps.hh>
@@ -102,6 +103,9 @@ namespace hpp {
       parent_t::add <SteeringMethodBuilder_t> ("Graph-SteeringMethodStraight",
           GraphSteeringMethod::create <core::SteeringMethodStraight>);
 
+      parent_t::add <PathOptimizerBuilder_t> ("KeypointsShortcut",
+          pathOptimization::Keypoints::create);
+
 #ifdef HPP_MANIPULATION_HAS_WHOLEBODY_STEP
       parent_t::add <PathOptimizerBuilder_t>
         ("Walkgen", wholebodyStep::SmallSteps::create);