diff --git a/applications/gui2/src/modules/camera.cpp b/applications/gui2/src/modules/camera.cpp
index 5eb1a71e6c254760481e51f757b5c4887e8ca461..66519caef37422f31399472058b79580ee30d1c7 100644
--- a/applications/gui2/src/modules/camera.cpp
+++ b/applications/gui2/src/modules/camera.cpp
@@ -77,7 +77,7 @@ void Camera::activate(ftl::data::FrameID id) {
 	std::mutex m;
 	std::condition_variable cv;
 
-	filter = io->feed()->filter(std::unordered_set<unsigned int>{id.frameset()}, {Channel::Left, Channel::Depth});
+	filter = io->feed()->filter(std::unordered_set<unsigned int>{id.frameset()}, {Channel::Left});
 	filter->on(
 		[this,&cv,&m, speaker = io->speaker()](ftl::data::FrameSetPtr fs){
 			std::atomic_store(&current_fs_, fs);
@@ -109,6 +109,7 @@ void Camera::activate(ftl::data::FrameID id) {
 
 void Camera::setChannel(Channel c) {
 	channel = c;
+	filter->select({c});
 	panel->setActiveChannel(channel);
 }
 
diff --git a/components/streams/include/ftl/streams/feed.hpp b/components/streams/include/ftl/streams/feed.hpp
index 6f8c856219a0dd20faab86855822aada2882c1cd..fbb6f9bab8c27b91e3c42023b3fc68ba14069ca7 100644
--- a/components/streams/include/ftl/streams/feed.hpp
+++ b/components/streams/include/ftl/streams/feed.hpp
@@ -33,6 +33,8 @@ public:
 		const std::unordered_set<ftl::codecs::Channel>& availableChannels() const { return channels_available_; };
 		const std::unordered_set<uint32_t>& sources() const { return sources_; };
 
+		Filter &select(const std::unordered_set<ftl::codecs::Channel> &cs);
+
 		void on(const ftl::data::FrameSetCallback &cb);
 		/** remove filter; any references/pointers become invalid */
 		void remove();
@@ -51,41 +53,6 @@ public:
 		std::vector<ftl::Handle> handles_;
 	};
 
-	class Renderer {
-		friend Feed;
-	public:
-		void on(const ftl::data::FrameSetCallback &cb);
-		/** set input. Renderer takes ownership of filter */
-		void addInput(Filter* filter, Eigen::Matrix4d pose);
-		void remove() { /* TODO */};
-		ftl::Handle onFrameSet(const ftl::data::FrameSetCallback &cb);
-
-	protected:
-		void run();
-		~Renderer();
-
-	private:
-		std::mutex mtx_;
-		std::condition_variable cv_;
-		std::atomic_bool run_;
-		int64_t ts_;
-
-		Renderer(Feed* feed, const std::string &name, uint32_t fsid, ftl::data::Pool *pool);
-		Feed* feed_;
-		ftl::data::FrameCreator creator_;
-		ftl::Handle builder_handle_;
-		std::vector<Filter*> filters_;
-
-		struct RendererInput {
-			ftl::data::FrameSetPtr fs;
-			Eigen::Matrix4d pose;
-		};
-
-		std::map<Filter*, RendererInput> input_;
-		std::unique_ptr<ftl::render::CUDARender> renderer_;
-		std::thread thread_;
-	};
-
 private:
 
 	// public methods acquire lock if necessary, private methods assume locking
@@ -146,8 +113,6 @@ public:
 	Filter* filter(const std::unordered_set<ftl::codecs::Channel> &channels);
 
 	void removeFilter(Filter* filter);
-
-	Renderer* render(Filter* filter, Eigen::Matrix4d pose, const std::string &name="");
 };
 
 }
diff --git a/components/streams/src/feed.cpp b/components/streams/src/feed.cpp
index 7355738839ce082f714fed7d2d0fb66f45acd88a..e7e3ab776ed063a4489597594d6b6734f51f2877 100644
--- a/components/streams/src/feed.cpp
+++ b/components/streams/src/feed.cpp
@@ -34,102 +34,11 @@ void Feed::Filter::on(const ftl::data::FrameSetCallback &cb) {
 	handles_.push_back(std::move(handler_.on(cb)));
 }
 
-////////////////////////////////////////////////////////////////////////////////
-
-Feed::Renderer::Renderer(Feed* feed, const std::string &name, uint32_t fsid, ftl::data::Pool *pool) :
-	feed_(feed), creator_(pool->creator<ftl::data::FrameCreator>(ftl::data::FrameID(fsid, 0))) {
-
-	renderer_ = std::unique_ptr<ftl::render::CUDARender>(
-		ftl::create<ftl::render::CUDARender>(feed, name)
-	);
-
-	ts_ = 1;
-
-	auto &frame_out = creator_.create(ts_).cast<ftl::rgbd::Frame>();
-
-	auto cam = ftl::rgbd::Camera();
-	cam.width = 1280;
-	cam.height = 720;
-	cam.fx = 700;
-	cam.fy = 700;
-	cam.minDepth = 0;
-	cam.maxDepth = 100;
-
-	frame_out.createChange<std::tuple<ftl::rgbd::Camera, ftl::codecs::Channel, int>>
-		(Channel::Calibration, ftl::data::ChangeType::FOREIGN) =
-			{cam, Channel::Calibration, 0};
-
-	frame_out.createChange<Eigen::Matrix4d>
-		(Channel::Pose, ftl::data::ChangeType::FOREIGN) = Eigen::Matrix4d::Identity();
-
-	run_ = true;
-	thread_ = std::move(std::thread(&Feed::Renderer::run, this));
-}
-
-void Feed::Renderer::addInput(Feed::Filter* filter, Eigen::Matrix4d pose) {
-	filter->on([this, filter, pose](const ftl::data::FrameSetPtr &fs) {
-		if (this->input_.count(filter) == 0) {
-			std::unique_lock<std::mutex> lk(mtx_);
-			input_[filter] = {fs, pose};
-		}
-		else {
-			input_[filter].fs = fs;
-		}
-
-		if (ts_ < fs->timestamp()) {
-			ts_ = fs->timestamp();
-		}
-
-		cv_.notify_one();
-		return true;
-	});
-}
-
-Feed::Renderer::~Renderer() {
-	run_ = false;
-	cv_.notify_all();
-	thread_.join();
-
-	builder_handle_.cancel();
-
-	for (auto& [filter, input] : input_) {
-		input.fs = nullptr;
-		filter->remove();
-	}
-}
-
-void Feed::Renderer::run() {
-	while(run_) {
-		std::unique_lock<std::mutex> lk(mtx_);
-		cv_.wait(lk);
-		if (!run_) { break; }
-
-		auto &frame_out = creator_.create(ts_).cast<ftl::rgbd::Frame>();
-
-		renderer_->begin(frame_out, Channel::Left);
-
-		for (const auto& [filter, input] : input_) {
-			if (!input.fs || !input.fs->hasAll({{Channel::Colour,
-												 Channel::Depth,
-												 Channel::Calibration}})) {
-				continue;
-			}
-
-			std::ignore = filter;
-			ftl::data::FrameSetPtr frame_in = input.fs;
-			renderer_->submit(
-				frame_in.get(),
-				ftl::codecs::Channels<0>(Channel::Colour),
-				input.pose);
-		}
-
-		renderer_->end();
-	}
-}
-
-ftl::Handle Feed::Renderer::onFrameSet(const ftl::data::FrameSetCallback &cb) {
-	ftl::Handler<int> h = ftl::Handler<int>();
-	return h.on([](int i){ return true; });
+Feed::Filter &Feed::Filter::select(const std::unordered_set<ftl::codecs::Channel> &cs) {
+	std::unique_lock<std::mutex> lk(feed_->mtx_);
+	channels_ = cs;
+	feed_->select();
+	return *this;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -440,17 +349,3 @@ uint32_t Feed::getID(const std::string &source) {
 	return fsid_lookup_.at(source);
 }
 
-Feed::Renderer* Feed::render(Feed::Filter* filter, Eigen::Matrix4d pose, const std::string &n) {
-	uint32_t fsid = allocateFrameSetId();
-	std::string name;
-	if (n.empty()) {
-		name = "renderer-" + std::to_string(fsid);
-	}
-	else {
-		name = n;
-	}
-
-	auto* renderer = new Feed::Renderer(this, name, fsid, pool_.get());
-	renderer->addInput(filter, pose);
-	return renderer;
-}