diff --git a/components/structures/include/ftl/data/new_frame.hpp b/components/structures/include/ftl/data/new_frame.hpp
index 68960b97579a8b14bff95a4f8bc5395bbc6a28e1..11cc408649bf7437466826d8c39f595161473e07 100644
--- a/components/structures/include/ftl/data/new_frame.hpp
+++ b/components/structures/include/ftl/data/new_frame.hpp
@@ -23,6 +23,21 @@ class Frame {
 	explicit Frame(Frame *parent) : parent_(parent) {};
 	~Frame() { flush(); };
 
+	Frame(Frame &&f) {
+		f.swapTo(*this);
+		f.reset();
+	}
+
+	Frame &operator=(Frame &&f) {
+		f.swapTo(*this);
+		f.reset();
+		return *this;
+	}
+
+	// Prevent frame copy, instead use a move.
+	Frame(const Frame &)=delete;
+	Frame &operator=(const Frame &)=delete;
+
 	inline bool has(ftl::codecs::Channel c) {
 		return data_.find(c) != data_.end() || (parent_ && parent_->has(c));
 	}
@@ -79,7 +94,9 @@ class Frame {
 
 	void merge(Frame &);
 
-	void swap(Frame &);
+	void swapTo(Frame &);
+
+	void swapChanged(Frame &);
 
 	void swapChannel(ftl::codecs::Channel, Frame &);
 
diff --git a/components/structures/src/new_frame.cpp b/components/structures/src/new_frame.cpp
index 52f40ad7e21600050da6134141e0e899d0184697..9d030ed8c259549d14b43b23d73db472b2612a74 100644
--- a/components/structures/src/new_frame.cpp
+++ b/components/structures/src/new_frame.cpp
@@ -30,3 +30,38 @@ bool Frame::flush() {
 	changed_.clear();
 	return true;
 }
+
+void Frame::merge(Frame &f) {
+	for (auto x : f) {
+		data_[x.first] = std::move(x.second);
+		touch(x.first);
+	}
+}
+
+void Frame::swapTo(Frame &f) {
+	for (auto x : *this) {
+		f.data_[x.first].swap(x.second);
+		f.changed_.emplace(x.first);
+		changed_.emplace(x.first);
+	}
+}
+
+void Frame::swapChanged(Frame &f) {
+	for (auto x : changed_) {
+		f.data_[x].swap(data_[x]);
+		f.changed_.emplace(x);
+	}
+}
+
+void Frame::swapChannel(ftl::codecs::Channel c, Frame &f) {
+	if (has(c)) {
+		f.data_[c].swap(data_[c]);
+		f.changed_.emplace(c);
+		changed_.emplace(c);
+	}
+}
+
+void Frame::clear() {
+	changed_.clear();
+	data_.clear();
+}