diff --git a/source/simple/motion/algorithm.hpp b/source/simple/motion/algorithm.hpp
index 8e15229e1294d638e091e24a8f6960ab31557a2d..e7f3f2863c6f921a74bdcca8643501b49eb53a20 100644
--- a/source/simple/motion/algorithm.hpp
+++ b/source/simple/motion/algorithm.hpp
@@ -7,31 +7,32 @@ namespace simple::motion
 {
 
 // welcome to proper looping
-// TODO: for known total duration mod(remaining, total) optimization
 template <typename Motion, typename Target>
 int loop(Target&& target, Motion& motion, typename Motion::duration delta)
 {
 	int count = 0;
-	// better way to write this??
-	while(true) if(auto [success, remaining] =
-		motion.move(std::forward<Target>(target), delta);
-		!success
-	)
+	while(auto result =
+		motion.move(std::forward<Target>(target), delta))
 	{
 		motion.reset();
-		delta = remaining;
+		delta = result.remaining;
 		++count;
 	}
-	else break;
 	return count;
 }
 
-template <typename It, typename Duration, typename Function>
+// and proper sequencing
+// NOTE: The done flag indicates that the last updated motion is done, not necessarily the whole input sequence.
+template <typename Stepper, typename Duration, typename Function>
 constexpr
-multi_advance_result<Duration, It> sequence(It begin, It end, Duration delta, Function&& advance)
+multi_advance_result<Duration, Stepper>
+sequence(Stepper begin, Stepper end, Duration delta, Function&& advance)
 {
-	support::range<It> updated{begin, begin};
-	for(; begin != end; ++begin)
+	assert(begin != end);
+
+	support::range<Stepper> updated{begin, begin};
+
+	do
 	{
 		auto result = std::apply(
 			std::forward<Function>(advance),
@@ -39,13 +40,15 @@ multi_advance_result<Duration, It> sequence(It begin, It end, Duration delta, Fu
 		);
 		++updated.upper();
 
-		delta = result.remaining;
+		if(result.remaining <= Duration{})
+			return {result, updated};
 
-		if(delta <= Duration{})
-			return {true, delta, updated};
+		delta = result.remaining;
+		++begin;
 	}
+	while(begin != end);
 
-	return {false, delta, updated};
+	return {true, delta, updated};
 }
 
 } // namespace simple::motion
diff --git a/source/simple/motion/common.hpp b/source/simple/motion/common.hpp
index add029bdb1eb76dd2eff1ba4e392bf7cea76ea5d..15abc907c7c40d193562dcae0a677deb68f31fb6 100644
--- a/source/simple/motion/common.hpp
+++ b/source/simple/motion/common.hpp
@@ -8,9 +8,9 @@ namespace simple::motion
 template <typename Duration>
 struct advance_result
 {
-	bool success = false;
+	bool done;
 	Duration remaining{};
-	explicit operator bool() { return success; }
+	explicit operator bool() { return done; } // negate?
 };
 
 template <typename Duration, typename It = size_t>
diff --git a/source/simple/motion/melody.hpp b/source/simple/motion/melody.hpp
index 29e2f5933b1e5c752e79ce125633aa69ff1f7ae3..adeda2a643711bc55f45ef46cf87eb56339cdbba 100644
--- a/source/simple/motion/melody.hpp
+++ b/source/simple/motion/melody.hpp
@@ -15,24 +15,21 @@ class melody
 	public:
 
 	using duration = std::common_type_t<typename Motions::duration...>;
+	using result_t = multi_advance_result<duration>;
 
 	melody() = default;
 	melody(Motions... motions) : movements{motions...} {}
 
 	bool done()
 	{
-		return std::get<sizeof...(Motions) - 1>(movements).done();
+		return current_index == sizeof...(Motions);
 	}
 
-	// auto advance(duration delta)
-	// {
-	// 	return advance(delta, {current_index, current_index + 1});
-	// }
-
 	// WIP: TODO: need complete restructure can't nest melodies
-	multi_advance_result<duration> advance(duration delta)
+	result_t advance(duration delta)
 	{
-		return sequence
+		assert(!done());
+		auto r = sequence
 		(
 			current_index, sizeof...(Motions),
 			delta,
@@ -47,6 +44,14 @@ class melody
 				);
 			}
 		);
+
+		bool last_done = r.done;
+		current_index = r.updated.upper() - !last_done;
+
+		if(last_done && current_index == sizeof...(Motions))
+			return {true, r.remaining, r.updated};
+
+		return {false, r.remaining, r.updated};
 	}
 
 	template <size_t... I>
@@ -67,8 +72,11 @@ class melody
 	}
 
 	template <typename T>
-	advance_result<duration> move(T& target, duration delta)
+	result_t move(T& target, duration delta)
 	{
+		if(done())
+			return {true, delta};
+
 		auto result = advance(delta);
 		support::apply_for(result.updated.upper()-1, [&target](auto&& movement)
 		{
@@ -79,8 +87,11 @@ class melody
 
 	template <typename... T,
 		std::enable_if_t<sizeof...(T) == sizeof...(Motions)>* = nullptr>
-	advance_result<duration> move(std::tuple<T...>&& targets, duration delta)
+	result_t move(std::tuple<T...>&& targets, duration delta)
 	{
+		if(done())
+			return {true, delta};
+
 		auto r = advance(delta);
 		support::apply_for(r.updated, [](auto&& movement, auto&& target)
 		{
@@ -91,9 +102,12 @@ class melody
 
 	template <typename T, size_t Size,
 		std::enable_if_t<Size == sizeof...(Motions)>* = nullptr>
-	advance_result<duration> move(std::array<T,Size>& targets, duration delta)
+	result_t move(std::array<T,Size>& targets, duration delta)
 	{
 		//TODO: mostly same as tuple version above
+		if(done())
+			return {true, delta};
+
 		auto r = advance(delta);
 		support::apply_for(r.updated, [](auto&& movement, auto&& target)
 		{
diff --git a/source/simple/motion/movement.hpp b/source/simple/motion/movement.hpp
index 87bcf13b194ef8a89a010c1cee8d13e146831969..1f55999760f8153381c6848a14b7d60a69713a9c 100644
--- a/source/simple/motion/movement.hpp
+++ b/source/simple/motion/movement.hpp
@@ -27,7 +27,8 @@ struct movement
 
 	decltype(auto) value()
 	{
-		auto ratio = total == Duration{} ? Ratio{} :
+		assert(total != Duration{}); // nice
+		auto ratio =
 			(Ratio{} + elapsed.count()) /
 			(Ratio{} + total.count());
 		using support::way;
@@ -41,14 +42,15 @@ struct movement
 
 	advance_result<Duration> advance(Duration delta)
 	{
+		assert(!done());
 		elapsed += delta;
 		if(done())
 		{
 			auto remaining = elapsed - total;
 			elapsed = total;
-			return {false, remaining};
+			return {true, remaining};
 		}
-		return {true};
+		return {false};
 	}
 
 	void reset()
@@ -58,6 +60,9 @@ struct movement
 
 	advance_result<Duration> move(Type& target, Duration delta)
 	{
+		if(done())
+			return {true, delta};
+
 		auto result = advance(delta);
 		target = value();
 		return result;
diff --git a/source/simple/motion/symphony.hpp b/source/simple/motion/symphony.hpp
index f1f22624b24aabe9e8398e8008acd56661c5f1f2..bc2dcb2cb06f7973378ab4611a3fe8f938059d93 100644
--- a/source/simple/motion/symphony.hpp
+++ b/source/simple/motion/symphony.hpp
@@ -34,38 +34,63 @@ struct variant : public std::variant<Motions...>
 
 };
 
-template <typename Motion,
-	template<typename T> typename Range =
-		std::vector>
+template <typename Range>
 class symphony
 {
 	public:
-	using duration = typename Motion::duration;
-	using iterator = typename Range<Motion>::iterator;
+	using iterator = decltype(std::begin(std::declval<Range&>()));
+	using motion = std::remove_reference_t<decltype(*std::declval<iterator>())>;
+	using duration = typename motion::duration;
+	using result_t = multi_advance_result<duration,iterator>;
 
-	explicit symphony(Range<Motion> motions) :
+	explicit symphony(Range motions) :
 		motions(std::move(motions)),
 		current(std::begin(this->motions))
 	{
 	}
 
-	multi_advance_result<duration, iterator>
-	advance(duration delta)
+	bool done()
 	{
-		return sequence
+		return current == std::end(motions);
+	}
+
+	void reset()
+	{
+		for(auto&& motion : motions)
+			motion.reset();
+		current = std::begin(motions);
+	}
+
+	result_t advance(duration delta)
+	{
+		assert(!done());
+		auto r = sequence
 		(
-			current, motions.end(),
+			current, std::end(motions),
 			delta,
-			[this](auto current, auto delta)
+			[](auto current, auto delta)
 			{
-				return (*current).advance(delta);
+				return current->advance(delta);
 			}
 		);
+
+		// TODO follows a duplicate code from melody melody
+		// maybe make another function sequence_update
+		bool last_done = r.done;
+		current = r.updated.upper() - !last_done;
+
+		if(last_done && current == std::end(motions))
+			return {true, r.remaining, r.updated};
+
+		return {false, r.remaining, r.updated};
 	}
 
 	template <typename TargetRange>
-	advance_result<duration> move(TargetRange&& target, duration delta)
+	result_t move(TargetRange&& target, duration delta)
 	{
+		if(done())
+			return {true, delta};
+
 		using std::begin;
 		using std::end;
 		using support::map_range;
@@ -82,8 +107,13 @@ class symphony
 		return r;
 	}
 
+	const Range& range()
+	{
+		return motions;
+	}
+
 	private:
-	Range<Motion> motions;
+	Range motions;
 	iterator current;
 
 };
diff --git a/unit_tests/loop.cpp b/unit_tests/loop.cpp
index 474328f437d4835098397ee05b7960493d25b3a2..f41ef0d57d81fd6b9e9ee282d8539a8d860c4607 100644
--- a/unit_tests/loop.cpp
+++ b/unit_tests/loop.cpp
@@ -1,12 +1,13 @@
 #include <chrono>
 #include "simple/motion/movement.hpp"
 #include "simple/motion/melody.hpp"
+#include "simple/motion/symphony.hpp"
 #include "simple/motion/algorithm.hpp"
 
 using namespace std::literals;
-using duration = std::chrono::duration<float>;
+// using duration = std::chrono::duration<float>;
+using duration = std::chrono::steady_clock::duration;
 using simple::support::way;
-using simple::motion::melody;
 
 // TODO: use rational, can't rely on float
 using movement = simple::motion::movement<duration,float,float>;
@@ -25,15 +26,33 @@ void BasicLoop()
 
 void LoopDontSkip()
 {
-	auto sequence = melody(
-		movement{10ms, 0,1},
-		movement{10ms, 0,1},
-		movement{10ms, 0,1},
-		movement{10ms, 0,1}
-	);
-	std::array<float,4> values{};
-	assert( 1 == loop(values, sequence, 60ms) ); // TODO: do 55ms with rational
-	assert(( values == std::array{1.f, 1.f, 1.f, 1.f} ));
+	{
+		auto sequence = simple::motion::melody(
+			movement{10ms, 0,1},
+			movement{10ms, 0,1},
+			movement{10ms, 0,1},
+			movement{10ms, 0,1}
+		);
+		std::array<float,4> values{};
+		assert( 1 == loop(values, sequence, 60ms) ); // TODO: do 55ms with rational
+		assert(( values == std::array{1.f, 1.f, 1.f, 1.f} ));
+		assert( 2 == loop(values, sequence, 90ms) );
+		assert(( values == std::array{1.f, 1.f, 1.f, 1.f} ));
+	}
+
+	{
+		auto sequence = simple::motion::symphony(std::array{
+			movement{10ms, 0,1},
+			movement{10ms, 0,1},
+			movement{10ms, 0,1},
+			movement{10ms, 0,1}
+		});
+		std::array<float,4> values{};
+		assert( 1 == loop(values, sequence, 60ms) ); // TODO: do 55ms with rational
+		assert(( values == std::array{1.f, 1.f, 1.f, 1.f} ));
+		assert( 2 == loop(values, sequence, 90ms) );
+		assert(( values == std::array{1.f, 1.f, 1.f, 1.f} ));
+	}
 }
 
 int main()
diff --git a/unit_tests/melody.cpp b/unit_tests/melody.cpp
index 509f9703c627a52b3f26a1dea259e76f81ac9716..1a9eadeff0ff2e4d7fa52a603845f37f0d43dcba 100644
--- a/unit_tests/melody.cpp
+++ b/unit_tests/melody.cpp
@@ -9,11 +9,12 @@ using duration = std::chrono::duration<float>;
 using simple::support::way;
 using simple::motion::melody;
 
-// TODO: use rational, can't rely on float
-using movement = simple::motion::movement<duration,float,float>;
 
 void SharpTurn()
 {
+	// TODO: use rational, can't rely on float
+	using movement = simple::motion::movement<duration,float,float>;
+
 	struct float2 { float x, y; };
 	float2 block{0,0};
 
@@ -23,22 +24,89 @@ void SharpTurn()
 	);
 
 
-	sharp_turn.move(std::forward_as_tuple(block.x, block.y), 500ms);
+	auto range = sharp_turn.move(std::forward_as_tuple(block.x, block.y), 500ms).updated;
 	assert( block.x == way(0.f,1.f, 0.5f / 1.25f) );
 	assert( block.y == 0.f );
+	assert( range.upper() - range.lower() == 1 );
 
-	sharp_turn.move(std::forward_as_tuple(block.x, block.y), 500ms);
+	range = sharp_turn.move(std::forward_as_tuple(block.x, block.y), 500ms).updated;
 	assert( block.x == way(0.f,1.f, 1.f / 1.25f) );
 	assert( block.y == 0.f );
+	assert( range.upper() - range.lower() == 1 );
 
-	sharp_turn.move(std::forward_as_tuple(block.x, block.y), 500ms);
+	range = sharp_turn.move(std::forward_as_tuple(block.x, block.y), 500ms).updated;
 	assert( block.x == way(0.f,1.f, 1.f) );
 	assert( block.y == way(0.f,1.f, 0.25f / 1.25f) );
+	assert( range.upper() - range.lower() == 2 );
+
+	range = sharp_turn.move(std::forward_as_tuple(block.x, block.y), 500ms).updated;
+	assert( block.x == way(0.f,1.f, 1.f) );
+	assert( block.y == way(0.f,1.f, 0.75f / 1.25f) );
+	assert( range.upper() - range.lower() == 1 );
+
+}
+
+void Advance()
+{
+	// TODO: use rational, can't rely on float
+	using movement = simple::motion::movement<std::chrono::milliseconds,float,float>;
+
+	auto sequence = melody(
+		movement{10ms, 0,1},
+		movement{10ms, 0,1},
+		movement{10ms, 0,1},
+		movement{10ms, 0,1}
+	);
+
+	auto result = sequence.advance(10ms);
+	assert( not result.done );
+	assert( result.updated.lower() == 0 );
+	assert( result.updated.upper() == 1 );
+	assert( result.remaining == 0ms );
+
+	result = sequence.advance(10ms);
+	assert( not result.done );
+	assert( result.updated.lower() == 1 );
+	assert( result.updated.upper() == 2 );
+	assert( result.remaining == 0ms );
+
+	result = sequence.advance(10ms);
+	assert( not result.done );
+	assert( result.updated.lower() == 2 );
+	assert( result.updated.upper() == 3 );
+	assert( result.remaining == 0ms );
+
+	result = sequence.advance(10ms);
+	assert( result.done );
+	assert( result.updated.lower() == 3 );
+	assert( result.updated.upper() == 4 );
+	assert( result.remaining == 0ms );
+
+	sequence.reset();
+
+	result = sequence.advance(15ms);
+	assert( not result.done );
+	assert( result.updated.lower() == 0 );
+	assert( result.updated.upper() == 2 );
+	assert( result.remaining == 0ms );
+
+	result = sequence.advance(15ms);
+	assert( not result.done );
+	assert( result.updated.lower() == 1 );
+	assert( result.updated.upper() == 3 );
+	assert( result.remaining == 0ms );
+
+	result = sequence.advance(15ms);
+	assert( result.done );
+	assert( result.updated.lower() == 3 );
+	assert( result.updated.upper() == 4 );
+	assert( result.remaining == 5ms );
 
 }
 
 int main()
 {
 	SharpTurn();
+	Advance();
 	return 0;
 }
diff --git a/unit_tests/symphony.cpp b/unit_tests/symphony.cpp
index 29992e888ab76eef3b2c93b51ae59e8fb9083f56..b6c35a63a421f85d32dd00fc1fff1a43e9bfe746 100644
--- a/unit_tests/symphony.cpp
+++ b/unit_tests/symphony.cpp
@@ -8,13 +8,14 @@ using duration = std::chrono::duration<float>;
 using simple::support::way;
 using simple::motion::symphony;
 
-// TODO: use rational, can't rely on float
-using movement = simple::motion::movement<duration,float,float>;
+
+template <typename T> class show_type;
 
 void SharpTurn()
 {
-	using float2 = std::array<float,2>;
-	float2 block{0,0};
+	// TODO: use rational, can't rely on float
+	using movement = simple::motion::movement<duration,float,float>;
+	std::array<float,2> block{0,0};
 
 	auto sharp_turn = symphony(std::vector<movement>{
 		movement{1250ms, 0,1},
@@ -22,22 +23,91 @@ void SharpTurn()
 	});
 
 
-	sharp_turn.move(block, 500ms);
+	auto range = sharp_turn.move(block, 500ms).updated;
 	assert( block[0] == way(0.f,1.f, 0.5f / 1.25f) );
 	assert( block[1] == 0.f );
+	assert( range.end() - range.begin() == 1 );
 
-	sharp_turn.move(block, 500ms);
+	range = sharp_turn.move(block, 500ms).updated;
 	assert( block[0] == way(0.f,1.f, 1.f / 1.25f) );
 	assert( block[1] == 0.f );
+	assert( range.end() - range.begin() == 1 );
 
-	sharp_turn.move(block, 500ms);
+	range = sharp_turn.move(block, 500ms).updated;
 	assert( block[0] == way(0.f,1.f, 1.f) );
 	assert( block[1] == way(0.f,1.f, 0.25f / 1.25f) );
+	assert( range.end() - range.begin() == 2 );
+
+	range = sharp_turn.move(block, 500ms).updated;
+	assert( block[0] == way(0.f,1.f, 1.f) );
+	assert( block[1] == way(0.f,1.f, 0.75f / 1.25f) );
+	assert( range.end() - range.begin() == 1 );
+
+}
+
+void Advance()
+{
+	// TODO: use rational, can't rely on float
+	using movement = simple::motion::movement<std::chrono::milliseconds,float,float>;
+
+	auto sequence = symphony(std::vector{
+		movement{10ms, 0,1},
+		movement{10ms, 0,1},
+		movement{10ms, 0,1},
+		movement{10ms, 0,1}
+	});
+
+	const auto begin = sequence.range().begin();
+
+	auto result = sequence.advance(10ms);
+	assert( not result.done );
+	assert( result.updated.lower() == begin+0 );
+	assert( result.updated.upper() == begin+1 );
+	assert( result.remaining == 0ms );
+
+	result = sequence.advance(10ms);
+	assert( not result.done );
+	assert( result.updated.lower() == begin+1 );
+	assert( result.updated.upper() == begin+2 );
+	assert( result.remaining == 0ms );
+
+	result = sequence.advance(10ms);
+	assert( not result.done );
+	assert( result.updated.lower() == begin+2 );
+	assert( result.updated.upper() == begin+3 );
+	assert( result.remaining == 0ms );
+
+	result = sequence.advance(10ms);
+	assert( result.done );
+	assert( result.updated.lower() == begin+3 );
+	assert( result.updated.upper() == begin+4 );
+	assert( result.remaining == 0ms );
+
+	sequence.reset();
+
+	result = sequence.advance(15ms);
+	assert( not result.done );
+	assert( result.updated.lower() == begin+0 );
+	assert( result.updated.upper() == begin+2 );
+	assert( result.remaining == 0ms );
+
+	result = sequence.advance(15ms);
+	assert( not result.done );
+	assert( result.updated.lower() == begin+1 );
+	assert( result.updated.upper() == begin+3 );
+	assert( result.remaining == 0ms );
+
+	result = sequence.advance(15ms);
+	assert( result.done );
+	assert( result.updated.lower() == begin+3 );
+	assert( result.updated.upper() == begin+4 );
+	assert( result.remaining == 5ms );
 
 }
 
 int main()
 {
 	SharpTurn();
+	Advance();
 	return 0;
 }