diff --git a/applications/reconstruct/src/main.cpp b/applications/reconstruct/src/main.cpp
index f3a92c93340de67bbee302a89dae93f01b550bcd..ddb17e42c10b36aa539501647b421aac7badbd21 100644
--- a/applications/reconstruct/src/main.cpp
+++ b/applications/reconstruct/src/main.cpp
@@ -136,7 +136,16 @@ static void run(ftl::Configurable *root) {
 	net->start();
 	net->waitConnections();
 	
-	// Check paths for an FTL file to load...
+	std::vector<int> sourcecounts;
+
+	// Add sources from the configuration file as a single group.
+	auto configuration_sources = root->getConfig()["sources"];
+	size_t configuration_size = configuration_sources.size();
+	if (configuration_size > 0) {
+		sourcecounts.push_back(configuration_size);
+	}
+
+	// Check paths for FTL files to load.
 	auto paths = (*root->get<nlohmann::json>("paths"));
 	for (auto &x : paths.items()) {
 		std::string path = x.value().get<std::string>();
@@ -162,12 +171,14 @@ static void run(ftl::Configurable *root) {
 			int N = root->value("N", 100);
 
 			// For each stream found, add a source object
-			for (int i=0; i<=min(max_stream,N-1); ++i) {
+			int count = min(max_stream+1, N);
+			for (int i=0; i<count; ++i) {
 				root->getConfig()["sources"].push_back(nlohmann::json{{"uri",std::string("file://") + path + std::string("#") + std::to_string(i)}});
 			}
+			sourcecounts.push_back(count);
 		}
 	}
-	
+
 	// Create a vector of all input RGB-Depth sources
 	auto sources = ftl::createArray<Source>(root, "sources", net);
 
@@ -175,7 +186,7 @@ static void run(ftl::Configurable *root) {
 		LOG(ERROR) << "No sources configured!";
 		return;
 	}
-	
+
 	ConfigProxy *configproxy = nullptr;
 	if (net->numberOfPeers() > 0) {
 		configproxy = new ConfigProxy(net); // TODO delete
@@ -235,15 +246,15 @@ static void run(ftl::Configurable *root) {
 		}
 	}
 
-	ftl::rgbd::FrameSet scene_A;  // Output of align process
-	ftl::rgbd::FrameSet scene_B;  // Input of render process
+	std::vector<ftl::rgbd::FrameSet> scene_A(sourcecounts.size());  // Output of align process
+	std::vector<ftl::rgbd::FrameSet> scene_B(sourcecounts.size());  // Input of render process
 
 	//ftl::voxhash::SceneRep *scene = ftl::create<ftl::voxhash::SceneRep>(root, "voxelhash");
 	ftl::rgbd::Streamer *stream = ftl::create<ftl::rgbd::Streamer>(root, "stream", net);
 	ftl::rgbd::VirtualSource *virt = ftl::create<ftl::rgbd::VirtualSource>(root, "virtual");
 	//root->set("tags", nlohmann::json::array({ root->getID()+"/virtual" }));
-	ftl::render::Triangular *splat = ftl::create<ftl::render::Triangular>(root, "renderer", &scene_B);
-	ftl::rgbd::Group *group = new ftl::rgbd::Group;
+	ftl::render::Triangular *splat = ftl::create<ftl::render::Triangular>(root, "renderer", &(scene_B[0]));
+	std::vector<ftl::rgbd::Group *> groups;
 	ftl::ILW *align = ftl::create<ftl::ILW>(root, "merge");
 
 	int o = root->value("origin_pose", 0) % sources.size();
@@ -258,7 +269,7 @@ static void run(ftl::Configurable *root) {
 		//virt->setTimestamp(scene_B.timestamp);
 		// Do we need to convert Lab to BGR?
 		if (align->isLabColour()) {
-			for (auto &f : scene_B.frames) {
+			for (auto &f : scene_B[0].frames) {
 				auto &col = f.get<cv::cuda::GpuMat>(Channel::Colour);
 				cv::cuda::cvtColor(col,col, cv::COLOR_Lab2BGR); // TODO: Add stream
 			}
@@ -268,28 +279,34 @@ static void run(ftl::Configurable *root) {
 	});
 	stream->add(virt);
 
-	for (size_t i=0; i<sources.size(); i++) {
-		Source *in = sources[i];
-		in->setChannel(Channel::Depth);
-		group->addSource(in);
+	size_t cumulative = 0;
+	for (auto c : sourcecounts) {
+		auto group = new ftl::rgbd::Group;
+		for (size_t i=cumulative; i<cumulative+c; i++) {
+			Source *in = sources[i];
+			in->setChannel(Channel::Depth);
+			group->addSource(in);
+		}
+		groups.push_back(group);
+		cumulative += c;
 	}
 
 	// ---- Recording code -----------------------------------------------------
 
 	std::ofstream fileout;
 	ftl::codecs::Writer writer(fileout);
-	auto recorder = [&writer,&group](ftl::rgbd::Source *src, const ftl::codecs::StreamPacket &spkt, const ftl::codecs::Packet &pkt) {
+	auto recorder = [&writer,&groups](ftl::rgbd::Source *src, const ftl::codecs::StreamPacket &spkt, const ftl::codecs::Packet &pkt) {
 		ftl::codecs::StreamPacket s = spkt;
 
 		// Patch stream ID to match order in group
-		s.streamID = group->streamID(src);
+		s.streamID = groups[0]->streamID(src);
 		writer.write(s, pkt);
 	};
 
 	root->set("record", false);
 
 	// Allow stream recording
-	root->on("record", [&group,&fileout,&writer,&recorder](const ftl::config::Event &e) {
+	root->on("record", [&groups,&fileout,&writer,&recorder](const ftl::config::Event &e) {
 		if (e.entity->value("record", false)) {
 			char timestamp[18];
 			std::time_t t=std::time(NULL);
@@ -297,17 +314,17 @@ static void run(ftl::Configurable *root) {
 			fileout.open(std::string(timestamp) + ".ftl");
 
 			writer.begin();
-			group->addRawCallback(std::function(recorder));
+			groups[0]->addRawCallback(std::function(recorder));
 
 			// TODO: Write pose+calibration+config packets
-			auto sources = group->sources();
+			auto sources = groups[0]->sources();
 			for (size_t i=0; i<sources.size(); ++i) {
 				//writeSourceProperties(writer, i, sources[i]);
 				sources[i]->inject(Channel::Calibration, sources[i]->parameters(), Channel::Left, sources[i]->getCapabilities());
 				sources[i]->inject(sources[i]->getPose()); 
 			}
 		} else {
-			group->removeRawCallback(recorder);
+			groups[0]->removeRawCallback(recorder);
 			writer.end();
 			fileout.close();
 		}
@@ -338,43 +355,45 @@ static void run(ftl::Configurable *root) {
 	pipeline1->append<ftl::operators::MultiViewMLS>("mvmls");
 	// Alignment
 
+	size_t size = groups.size();
+	for (size_t i = 0; i < size; ++i) {
+		groups[i]->setLatency(4);
+		groups[i]->setName("ReconGroup-" + std::to_string(i));
+		groups[i]->sync([splat,virt,&busy,&slave,&scene_A,&scene_B,&align,controls,pipeline1,i](ftl::rgbd::FrameSet &fs) -> bool {
+			//cudaSetDevice(scene->getCUDADevice());
 
-	group->setLatency(4);
-	group->setName("ReconGroup");
-	group->sync([splat,virt,&busy,&slave,&scene_A,&scene_B,&align,controls,pipeline1](ftl::rgbd::FrameSet &fs) -> bool {
-		//cudaSetDevice(scene->getCUDADevice());
-
-		//if (slave.isPaused()) return true;
-		if (controls->value("paused", false)) return true;
-		
-		if (busy) {
-			LOG(INFO) << "Group frameset dropped: " << fs.timestamp;
-			return true;
-		}
-		busy = true;
+			//if (slave.isPaused()) return true;
+			if (controls->value("paused", false)) return true;
+			
+			if (busy) {
+				LOG(INFO) << "Group frameset dropped: " << fs.timestamp;
+				return true;
+			}
+			busy = true;
 
-		// Swap the entire frameset to allow rapid return
-		fs.swapTo(scene_A);
+			// Swap the entire frameset to allow rapid return
+			fs.swapTo(scene_A[i]);
 
-		ftl::pool.push([&scene_B,&scene_A,&busy,&slave,&align, pipeline1](int id) {
-			//cudaSetDevice(scene->getCUDADevice());
-			// TODO: Release frameset here...
-			//cudaSafeCall(cudaStreamSynchronize(scene->getIntegrationStream()));
+			ftl::pool.push([&scene_B,&scene_A,&busy,&slave,&align,pipeline1,i](int id) {
+				//cudaSetDevice(scene->getCUDADevice());
+				// TODO: Release frameset here...
+				//cudaSafeCall(cudaStreamSynchronize(scene->getIntegrationStream()));
 
-			UNIQUE_LOCK(scene_A.mtx, lk);
+				UNIQUE_LOCK(scene_A[i].mtx, lk);
 
-			pipeline1->apply(scene_A, scene_A, 0);
-			align->process(scene_A);
+				pipeline1->apply(scene_A[i], scene_A[i], 0);
+				align->process(scene_A[i]);
 
 
-			// TODO: To use second GPU, could do a download, swap, device change,
-			// then upload to other device. Or some direct device-2-device copy.
-			scene_A.swapTo(scene_B);
-			LOG(INFO) << "Align complete... " << scene_A.timestamp;
-			busy = false;
+				// TODO: To use second GPU, could do a download, swap, device change,
+				// then upload to other device. Or some direct device-2-device copy.
+				scene_A[i].swapTo(scene_B[i]);
+				LOG(INFO) << "Align complete... " << scene_A[i].timestamp;
+				busy = false;
+			});
+			return true;
 		});
-		return true;
-	});
+	}
 
 	LOG(INFO) << "Start timer";
 	ftl::timer::start(true);
@@ -394,7 +413,9 @@ static void run(ftl::Configurable *root) {
 	delete stream;
 	delete virt;
 	delete net;
-	delete group;
+	for (auto g : groups) {
+		delete g;
+	}
 
 	ftl::config::cleanup();  // Remove any last configurable objects.
 	LOG(INFO) << "Done.";
diff --git a/components/common/cpp/src/configuration.cpp b/components/common/cpp/src/configuration.cpp
index aea0f1b209019332334a8530594d6ba1a84e4587..12ca9163d2c05ef23bfc6ca74c8345204f7bf60d 100644
--- a/components/common/cpp/src/configuration.cpp
+++ b/components/common/cpp/src/configuration.cpp
@@ -581,11 +581,11 @@ Configurable *ftl::config::configure(int argc, char **argv, const std::string &r
 	// Process Arguments
 	auto options = ftl::config::read_options(&argv, &argc);
 	
-	vector<string> paths;
+	vector<string> paths(argc);
 	while (argc-- > 0) {
-		paths.push_back(argv[0]);
+		paths[argc] = argv[argc];
 	}
-	
+
 	if (!findConfiguration(options["config"], paths)) {
 		LOG(FATAL) << "Could not find any configuration!";
 	}