diff --git a/bunny.cpp b/bunny.cpp
index cd7a7e9598b8d2cbb3c384d2b28be2256e301ed9..3528b35defd0b78445c3791edb1cea6ccb120333 100644
--- a/bunny.cpp
+++ b/bunny.cpp
@@ -157,8 +157,8 @@ range2f nose_bounds;
 
 const framebuffer * furbuffer;
 
-using poke_motion = motion<float2, quadratic_curve>;
-symphony<poke_motion, poke_motion> poke;
+using poke_motion = movement<float2, motion::quadratic_curve>;
+melody<poke_motion, poke_motion> poke;
 
 void start(Program& program)
 {
@@ -255,7 +255,7 @@ void start(Program& program)
 				float fade = std::min(ratio*fade_in, (1-ratio)*fade_out);
 				return lerp(0.f,std::sin(ratio * 170 * poke_ratio), std::min(fade, 1.f));
 			}, 100ms}, 0);
-			poke = symphony(
+			poke = melody(
 				poke_motion{float2::zero(), offset/2, 100ms},
 				poke_motion{offset/2, float2::zero(), 100ms}
 			);
diff --git a/circular_maze.cpp b/circular_maze.cpp
index 75bb88ee1a3c194189f7b70671819280b503fe64..0832939930b2b5d3db964b02d546fe6ac63fb1ac 100644
--- a/circular_maze.cpp
+++ b/circular_maze.cpp
@@ -388,10 +388,10 @@ class circular_maze
 
 } maze(float2::one(400));
 
-using radial_motion_t = motion<float, quadratic_curve>;
-using circular_motion_t = motion<float, quadratic_curve>;
+using radial_motion_t = movement<float, motion::quadratic_curve>;
+using circular_motion_t = movement<float, motion::quadratic_curve>;
 
-symphony<circular_motion_t, radial_motion_t>
+melody<circular_motion_t, radial_motion_t>
 radial_motion;
 
 struct radial_movement
@@ -505,7 +505,7 @@ void start(Program& program)
 
 				auto circular_distance = mod_difference(maze.current_angle, wrap(3/4.f - movement.path, 1.f), 1.f);
 				auto radial_distance = movement.level - maze.player_level;
-				radial_motion = symphony(
+				radial_motion = melody(
 					circular_motion_t{
 						maze.current_angle,
 						maze.current_angle + circular_distance,
diff --git a/common/motion.hpp b/common/motion.hpp
deleted file mode 100644
index b7e72e09cc0d21c7ea61609d7fe3339d045306bc..0000000000000000000000000000000000000000
--- a/common/motion.hpp
+++ /dev/null
@@ -1,194 +0,0 @@
-// what even is a tween?
-// shorhand for between??
-// what even is between???
-
-#ifndef COMMON_MOTION_HPP
-#define COMMON_MOTION_HPP
-
-float linear_curve(float x) { return x; };
-float quadratic_curve(float x) { return x*x; };
-float cubic_curve(float x) { return x*x*x; };
-
-using curve_t = decltype(&linear_curve);
-
-struct advance_result
-{
-	bool success = false;
-	Program::duration remaining = 0s;
-	explicit operator bool() { return success; }
-};
-struct multi_advance_result : public advance_result
-{
-	range<size_t> updated = range<size_t>{0,0};
-	using advance_result::operator bool;
-};
-
-template <typename Type, curve_t curve = linear_curve>
-struct motion
-{
-	Type start;
-	Type end;
-	Program::duration total = 0s;
-	Program::duration elapsed = 0s;
-
-	decltype(auto) value()
-	{
-		auto ratio = total == 0s ? 0 : elapsed.count()/total.count();
-		return lerp(start, end, curve(ratio));
-	}
-
-	bool done()
-	{
-		return elapsed >= total;
-	}
-
-	advance_result advance(Program::duration delta)
-	{
-		elapsed += delta;
-		if(done())
-		{
-			elapsed = total;
-			return {false};
-		}
-		return {true};
-	}
-
-	void reset()
-	{
-		elapsed = 0s;
-	}
-
-	bool move(Type& target, Program::duration delta)
-	{
-		bool success = advance(delta).success;
-		target = value();
-		return !success;
-	}
-};
-
-// welcome to proper looping
-// TODO: for know total duration mod(remaining, total)
-// TODO: for unknown total while(move, !success){delta = remaining}
-template <typename Motion, typename Target>
-bool loop(Target&& target, Motion& motion, Program::duration delta)
-{
-	if(auto [success, remaining] = motion.move(std::forward<Target>(target), delta);
-		!success)
-	{
-		motion.reset();
-		motion.advance(remaining);
-		return true;
-	}
-	return false;
-}
-
-template <typename... Motions>
-class ensemble // TODO
-{
-	std::tuple<Motions...> instruments;
-	bool done();
-	bool advance(Program::duration delta);
-	void reset();
-	template <typename T>
-	bool move(T& target, Program::duration delta);
-};
-
-template <typename... Motions>
-class symphony
-{
-	std::tuple<Motions...> movements;
-	size_t current_index = 0;
-
-	template<typename F, size_t I = sizeof...(Motions) - 1>
-	void for_all(F&& f)
-	{
-		static_assert(I >= 0 && I < sizeof...(Motions));
-		std::forward<F>(f)(std::get<I>(movements));
-		if constexpr (I > 0)
-			for_all<F, I-1>(std::forward<F>(f));
-	}
-
-	multi_advance_result advance(Program::duration delta, range<size_t> updated)
-	{
-		auto [success, remaining] = support::apply_for(current_index, [&delta](auto&& movement)
-		{
-			return movement.advance(delta);
-		}, movements);
-
-		if(!success)
-		{
-			if(current_index == sizeof...(Motions) - 1)
-				return {success, remaining, updated};
-
-			++current_index;
-
-			if(remaining > 0s)
-				return advance(remaining, {updated.lower(), updated.upper()+1});
-			else
-				return {true, remaining, updated};
-		}
-		else
-		{
-			return {success, remaining, updated};
-		}
-	}
-
-	public:
-
-	symphony() = default;
-	symphony(Motions... motions) : movements{motions...} {}
-
-	bool done()
-	{
-		return std::get<sizeof...(Motions) - 1>(movements).done();
-	}
-
-	auto advance(Program::duration delta)
-	{
-		return advance(delta, {current_index, current_index + 1});
-	}
-
-	template <size_t... I>
-	std::tuple<decltype(std::declval<Motions>().value())...> value(std::index_sequence<I...>)
-	{
-		return {std::get<I>(movements).value()...};
-	}
-
-	std::tuple<decltype(std::declval<Motions>().value())...> value()
-	{
-		return value(std::make_index_sequence<sizeof...(Motions)>{});
-	}
-
-	void reset()
-	{
-		for_all([](auto& movement) {movement.reset();});
-		current_index = 0;
-	}
-
-	template <typename T>
-	advance_result move(T& target, Program::duration delta)
-	{
-		auto result = advance(delta);
-		support::apply_for(current_index, [&target](auto&& movement)
-		{
-			target = movement.value();
-		}, movements);
-		return result;
-	}
-
-	template <typename... T,
-		 std::enable_if_t<sizeof...(T) == sizeof...(Motions)>* = nullptr>
-	advance_result move(std::tuple<T...>&& targets, Program::duration delta)
-	{
-		auto r = advance(delta);
-		support::apply_for(r.updated, [](auto&& movement, auto&& target)
-		{
-			target = movement.value();
-		}, movements, std::forward<std::tuple<T...>>(targets));
-		return r;
-	}
-};
-
-class conductor; // TODO: dynamic symphony with all strings attached
-
-#endif /* end of include guard */
diff --git a/common/sketchbook.hpp b/common/sketchbook.hpp
index 48e6bcb56c79edf41c172e2ae1d13f768d06ce28..5ab76f6576cf3940d431f8d4e5bb24a4eb549632 100644
--- a/common/sketchbook.hpp
+++ b/common/sketchbook.hpp
@@ -15,6 +15,7 @@
 #include "simple/musical.hpp"
 #include "simple/interactive/initializer.h"
 #include "simple/interactive/event.h"
+#include "simple/motion.hpp"
 
 #include "simple_vg.h"
 #include "simple_vg.cpp" // TODO: woops, don't do this
@@ -197,10 +198,11 @@ class Program
 	friend int main(int argc, char* argv[]);
 };
 
-inline void process_events(Program&);
+template <typename T, motion::curve_t<float> curve = motion::linear_curve<float>>
+using movement = motion::movement<Program::duration, T, float, curve>;
+using motion::melody;
 
-// TODO: move dependent stuff out of here and include this at the top
-#include "motion.hpp"
+inline void process_events(Program&);
 
 void start(Program&);
 
diff --git a/tools/setup/init.sh b/tools/setup/init.sh
index e4dd73f22ab03ed12d5cf90489000b88f0786eb5..d6b3caa77c7f393cc49fdc404d43452d7b1f5706 100755
--- a/tools/setup/init.sh
+++ b/tools/setup/init.sh
@@ -5,6 +5,7 @@ rm -rf .dependencies/*
 cd .dependencies
 git clone https://notabug.org/namark/cpp_tools
 git clone https://notabug.org/namark/libsimple_support
+git clone https://notabug.org/namark/libsimple_motion
 git clone https://notabug.org/namark/libsimple_geom
 git clone https://notabug.org/namark/libsimple_sdlcore
 git clone https://notabug.org/namark/libsimple_graphical
diff --git a/tools/setup/install.sh b/tools/setup/install.sh
index 28b2a397065297a9e4aecd29dbde68566d5646ff..c49372f1e63ffa7cf42e1fb1950474a245a91059 100755
--- a/tools/setup/install.sh
+++ b/tools/setup/install.sh
@@ -3,6 +3,7 @@ cd .dependencies
 
 cd cpp_tools
 make PREFIX=../libsimple_support ../libsimple_support/include/make_templates/header_only_lib
+make PREFIX=../libsimple_motion ../libsimple_support/include/make_templates/header_only_lib
 make PREFIX=../libsimple_geom ../libsimple_geom/include/make_templates/header_only_lib
 make PREFIX=../libsimple_sdlcore ../libsimple_sdlcore/include/make_templates/static_lib
 make PREFIX=../libsimple_graphical ../libsimple_graphical/include/make_templates/static_lib
@@ -19,6 +20,10 @@ make install PREFIX=../libsimple_interactive "$@"
 make install PREFIX=../libsimple_musical "$@"
 cd ..
 
+cd libsimple_motion
+make install PREFIX=../../  "$@"
+cd ..
+
 cd libsimple_geom
 make install PREFIX=../../  "$@"
 make install PREFIX=../libsimple_sdlcore "$@"
diff --git a/tools/setup/update.sh b/tools/setup/update.sh
index c5d368c56f9ad263c179a3e9a1a6424e3c1004d1..97d4cea580e3f3a8f9a60fc269caf0ae06d7ce7c 100755
--- a/tools/setup/update.sh
+++ b/tools/setup/update.sh
@@ -10,6 +10,10 @@ cd libsimple_support
 git pull -r
 cd ..
 
+cd libsimple_motion
+git pull -r
+cd ..
+
 cd libsimple_geom
 git pull -r
 cd ..
diff --git a/various_motion.cpp b/various_motion.cpp
index daa5a89fa0e3acb067ad07837fe25a140fa2c296..762fe48889df2d517238d3dca90374c43bdf6f6a 100644
--- a/various_motion.cpp
+++ b/various_motion.cpp
@@ -1,19 +1,19 @@
 #include "common/sketchbook.hpp"
 
-using quadratic_motion = motion<float, quadratic_curve>;
+using quadratic_move = movement<float, motion::quadratic_curve>;
 
 float block = 0;
-auto diagonal_back_and_forth = symphony(
-	quadratic_motion{0,1, 500ms},
-	quadratic_motion{1,0, 500ms}
+auto diagonal_back_and_forth = melody(
+	quadratic_move{0,1, 500ms},
+	quadratic_move{1,0, 500ms}
 );
 
 float2 block2 = float2::zero();
-auto square_around = symphony(
-	quadratic_motion{0,1, 500ms},
-	quadratic_motion{0,1, 500ms},
-	quadratic_motion{1,0, 500ms},
-	quadratic_motion{1,0, 500ms}
+auto square_around = melody(
+	quadratic_move{0,1, 500ms},
+	quadratic_move{0,1, 500ms},
+	quadratic_move{1,0, 500ms},
+	quadratic_move{1,0, 500ms}
 );
 
 void start(Program& program)