diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6c66d67..8f47cb5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,5 +1,5 @@
 cmake_minimum_required(VERSION 3.25)
-project(image-gp-2 VERSION 0.0.7)
+project(image-gp-2 VERSION 0.0.8)
 
 option(ENABLE_ADDRSAN "Enable the address sanitizer" OFF)
 option(ENABLE_UBSAN "Enable the ub sanitizer" OFF)
diff --git a/include/gp_system.h b/include/gp_system.h
index ff58e92..6150081 100644
--- a/include/gp_system.h
+++ b/include/gp_system.h
@@ -27,7 +27,7 @@ void run_step();
 
 bool should_terminate();
 
-image_storage_t& get_image(blt::size_t index);
+std::array<image_pixel_t, IMAGE_DIMENSIONS * IMAGE_DIMENSIONS * 3>& get_image(blt::size_t index);
 
 void cleanup();
 
diff --git a/include/image_storage.h b/include/image_storage.h
index 819026c..acba1b9 100644
--- a/include/image_storage.h
+++ b/include/image_storage.h
@@ -68,10 +68,11 @@ struct image_cleaner_t
 };
 
 inline image_cleaner_t g_image_list;
+inline std::atomic_uint64_t g_image_counter = 0;
 
 struct image_t
 {
-	image_t()
+	image_t(): id(++g_image_counter)
 	{
 		image_storage_t* front = nullptr;
 		{
@@ -87,8 +88,14 @@ struct image_t
 		else
 			data = new image_storage_t;
 		++g_allocated_blocks;
+
+		BLT_TRACE("Allocated {}!", id);
 	}
 
+	image_t(const image_t& other) = default;
+
+	image_t& operator=(const image_t& other) = default;
+
 	void drop()
 	{
 		{
@@ -97,6 +104,7 @@ struct image_t
 		}
 		data = nullptr;
 		++g_deallocated_blocks;
+		BLT_TRACE("Deallocated {}!", id);
 	}
 
 	[[nodiscard]] void* as_void_const() const
@@ -129,6 +137,7 @@ struct image_t
 
 private:
 	image_storage_t* data;
+	blt::size_t id;
 };
 
 #endif //IMAGE_STORAGE_H
diff --git a/src/gp_system.cpp b/src/gp_system.cpp
index 1ebb2e2..5667970 100644
--- a/src/gp_system.cpp
+++ b/src/gp_system.cpp
@@ -53,6 +53,7 @@ void fitness_func(const tree_t& tree, fitness_t& fitness, const blt::size_t inde
 	fitness.adjusted_fitness = -fitness.standardized_fitness;
 }
 
+template <typename T>
 void setup_operations(gp_program* program)
 {
 	static operation_t op_image_x([]() {
@@ -73,15 +74,13 @@ void setup_operations(gp_program* program)
 		}
 		return ret;
 	});
-	static operation_t op_image_xy([]() {
+	static auto op_image_ephemeral = operation_t([program]() {
 		image_t ret;
-		for (blt::size_t x = 0; x < IMAGE_DIMENSIONS; ++x)
-		{
-			for (blt::size_t y = 0; y < IMAGE_DIMENSIONS; ++y)
-				ret.get_data().get(x, y) = static_cast<float>(x + y) / static_cast<float>((IMAGE_DIMENSIONS - 1) * (IMAGE_DIMENSIONS - 1));
-		}
+		const auto value = program->get_random().get_float();
+		for (auto& v : ret.get_data().data)
+			v = value;
 		return ret;
-	});
+	}).set_ephemeral();
 	static operation_t op_image_blend([](const image_t& a, const image_t& b, const float f) {
 		const auto blend = std::min(std::max(f, 0.0f), 1.0f);
 		const auto beta = 1.0f - blend;
@@ -91,7 +90,32 @@ void setup_operations(gp_program* program)
 		cv::Mat dst{IMAGE_DIMENSIONS, IMAGE_DIMENSIONS, CV_32FC1, ret.get_data().data.data()};
 		addWeighted(src1, blend, src2, beta, 0.0, dst);
 		return ret;
-	}, "blend");
+	}, "blend_image");
+	static operation_t op_image_sin([](const image_t& a) {
+		image_t ret;
+		for (const auto& [i, v] : blt::enumerate(std::as_const(a.get_data().data)))
+			ret.get_data().data[i] = std::sin(v);
+		return ret;
+	}, "sin_image");
+	static operation_t op_image_cos([](const image_t& a) {
+		image_t ret;
+		for (const auto& [i, v] : blt::enumerate(std::as_const(a.get_data().data)))
+			ret.get_data().data[i] = std::cos(v);
+		return ret;
+	}, "cos_image");
+	static operation_t op_image_log([](const image_t& a) {
+		image_t ret;
+		for (const auto& [i, v] : blt::enumerate(std::as_const(a.get_data().data)))
+		{
+			if (blt::f_equal(v, 0))
+				ret.get_data().data[i] = 0;
+			else if (v < 0)
+				ret.get_data().data[i] = -std::log(-v);
+			else
+				ret.get_data().data[i] = std::log(v);
+		}
+		return ret;
+	}, "sin_image");
 	static operation_t op_sin([](const float a) {
 		return std::sin(a);
 	}, "sin_float");
@@ -113,57 +137,85 @@ void setup_operations(gp_program* program)
 	}, "lit_float").set_ephemeral();
 
 	operator_builder builder{};
-	builder.build(make_add<image_t>(), make_sub<image_t>(), make_mul<image_t>(), make_div<image_t>(), op_image_x, op_image_y, op_image_xy,
-				make_add<float>(), make_sub<float>(), make_mul<float>(), make_prot_div<float>(), op_sin, op_cos, op_exp, op_log, lit);
+	builder.build(make_add<image_t>(), make_sub<image_t>(), make_mul<image_t>(), make_div<image_t>(), op_image_x, op_image_y, op_image_sin,
+				 op_image_cos, op_image_log, make_add<float>(), make_sub<float>(), make_mul<float>(), make_prot_div<float>(),
+				op_sin, op_cos, op_exp, op_log, lit);
 	program->set_operations(builder.grab());
 }
 
 void setup_gp_system(const blt::size_t population_size)
 {
 	prog_config_t config{};
-	config.population_size = population_size;
-	config.elites = 2;
-	program = new gp_program{
-		[]() {
-			return std::random_device()();
-		},
-		config
-	};
+	config.set_pop_size(1);
+	config.set_elite_count(0);
+	config.set_thread_count(0);
+	// config.set_crossover_chance(0);
+	// config.set_mutation_chance(0);
+	// config.set_reproduction_chance(0);
+
+	const auto rand = std::random_device()();
+	BLT_INFO("Random Seed: {}", rand);
+	for (auto& program : programs)
+	{
+		program = new gp_program{rand, config};
+	}
+	setup_operations<struct p1>(programs[0]);
+	setup_operations<struct p2>(programs[1]);
+	setup_operations<struct p3>(programs[2]);
 
 	images.resize(population_size);
-	setup_operations();
+
 	static auto sel = select_tournament_t{};
-	program->generate_initial_population(program->get_typesystem().get_type<image_t>().id());
-	program->setup_generational_evaluation(fitness_func, sel, sel, sel);
+
+	for (const auto program : programs)
+		program->generate_initial_population(program->get_typesystem().get_type<image_t>().id());
+
+	programs[0]->setup_generational_evaluation(fitness_func<0>, sel, sel, sel);
+	programs[1]->setup_generational_evaluation(fitness_func<1>, sel, sel, sel);
+	programs[2]->setup_generational_evaluation(fitness_func<2>, sel, sel, sel);
 }
 
 void run_step()
 {
-	BLT_TRACE("------------\\{Begin Generation {}}------------", program->get_current_generation());
+	BLT_TRACE("------------\\{Begin Generation {}}------------", programs[0]->get_current_generation());
 	BLT_TRACE("Creating next generation");
-	program->create_next_generation();
+	for (const auto program : programs)
+		program->create_next_generation();
 	BLT_TRACE("Move to next generation");
-	program->next_generation();
+	for (const auto program : programs)
+		program->next_generation();
 	BLT_TRACE("Evaluate Fitness");
-	program->evaluate_fitness();
-	const auto& stats = program->get_population_stats();
-	BLT_TRACE("Avg Fit: {:0.6f}, Best Fit: {:0.6f}, Worst Fit: {:0.6f}, Overall Fit: {:0.6f}", stats.average_fitness.load(std::memory_order_relaxed),
-			stats.best_fitness.load(std::memory_order_relaxed), stats.worst_fitness.load(std::memory_order_relaxed),
-			stats.overall_fitness.load(std::memory_order_relaxed));
+	for (const auto program : programs)
+		program->evaluate_fitness();
+	for (const auto [i, program] : blt::enumerate(programs))
+	{
+		const auto& stats = program->get_population_stats();
+		if (i == 0)
+			BLT_TRACE("Channel Red");
+		else if (i == 1)
+			BLT_TRACE("Channel Green");
+		else
+			BLT_TRACE("Channel Blue");
+		BLT_TRACE("\tAvg Fit: {:0.6f}, Best Fit: {:0.6f}, Worst Fit: {:0.6f}, Overall Fit: {:0.6f}",
+				stats.average_fitness.load(std::memory_order_relaxed), stats.best_fitness.load(std::memory_order_relaxed),
+				stats.worst_fitness.load(std::memory_order_relaxed), stats.overall_fitness.load(std::memory_order_relaxed));
+	}
+
 	BLT_TRACE("----------------------------------------------");
 }
 
 bool should_terminate()
 {
-	return program->should_terminate();
+	return programs[0]->should_terminate() || programs[1]->should_terminate() || programs[2]->should_terminate();
 }
 
-image_storage_t& get_image(blt::size_t index)
+std::array<image_pixel_t, IMAGE_DIMENSIONS * IMAGE_DIMENSIONS * 3>& get_image(const blt::size_t index)
 {
 	return images[index];
 }
 
 void cleanup()
 {
-	delete program;
+	for (const auto program : programs)
+		delete program;
 }
diff --git a/src/main.cpp b/src/main.cpp
index 431d2a3..14b2ad1 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -70,7 +70,7 @@ void update(const blt::gfx::window_data& data)
 	ImGui::SetNextWindowSize(ImVec2(ImGui::GetIO().DisplaySize.x, ImGui::GetIO().DisplaySize.y));
 	ImGui::Begin("MainWindow", nullptr,
 				ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse |
-				ImGuiWindowFlags_NoBringToFrontOnFocus);
+				ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoBackground);
 
 	// Create the tab bar
 	if (ImGui::BeginTabBar("MainTabs"))
@@ -132,7 +132,26 @@ void update(const blt::gfx::window_data& data)
 	ImGui::End();
 
 	for (blt::size_t i = 0; i < population_size; i++)
-		gl_images[i]->upload(get_image(i).data.data(), IMAGE_DIMENSIONS, IMAGE_DIMENSIONS, GL_RGBA, GL_FLOAT);
+	{
+		auto& image = get_image(i);
+		float min = std::numeric_limits<float>::max();
+		float max = std::numeric_limits<float>::min();
+
+		for (auto& pixel : image)
+		{
+			if (std::isnan(pixel) || std::isinf(pixel))
+				pixel = 0;
+			if (pixel > max)
+				max = pixel;
+			if (pixel < min)
+				min = pixel;
+		}
+
+		for (auto& pixel : image)
+			pixel = (pixel - min) / (max - min);
+
+		gl_images[i]->upload(get_image(i).data(), IMAGE_DIMENSIONS, IMAGE_DIMENSIONS, GL_RGB, GL_FLOAT);
+	}
 
 	constexpr int images_x = 10;
 	constexpr int images_y = 6;
@@ -142,12 +161,12 @@ void update(const blt::gfx::window_data& data)
 		{
 			constexpr float padding_x = 32;
 			constexpr float padding_y = 32;
-			const float img_width = (static_cast<float>(data.width) - padding_x * 2 - padding_x * (images_x-1) - 256) / images_x;
-			const float img_height = (static_cast<float>(data.height) - padding_y * 2 - padding_y * (images_y-1) - 32) / images_y;
+			const float img_width = (static_cast<float>(data.width) - padding_x * 2 - padding_x * (images_x - 1) - 256) / images_x;
+			const float img_height = (static_cast<float>(data.height) - padding_y * 2 - padding_y * (images_y - 1) - 32) / images_y;
 			const float x = 256 + static_cast<float>(i) * img_width + padding_x * static_cast<float>(i) + img_width;
 			const float y = static_cast<float>(data.height) - (16 + static_cast<float>(j) * img_height + padding_y * static_cast<float>(j) +
 				img_height);
-			renderer_2d.drawRectangle(blt::gfx::rectangle2d_t{x, y, img_width, img_height}, std::to_string(i * j));
+			renderer_2d.drawRectangle(blt::gfx::rectangle2d_t{x, y, img_width, img_height}, std::to_string(i * images_y + j));
 		}
 	}