diff --git a/src/common/state.rs b/src/common/state.rs
index ad99308ef22e5e9494775c37aecf8f2aa16b8e00..a644fad9f3b067d61eb08f2e0806756f7ba657aa 100644
--- a/src/common/state.rs
+++ b/src/common/state.rs
@@ -37,7 +37,7 @@ pub struct Changeset<C: blockchain::Config> {
 
 #[derive(Event)]
 pub struct BlockchainEvent<C: blockchain::Config> {
-	changeset: Changeset<C>,
+	pub changeset: Changeset<C>,
 }
 
 #[derive(Clone)]
diff --git a/src/main.rs b/src/main.rs
index bb0ee8418f91e4ea0447413ccbd673d3d5965808..c714698c78953c388a9e3c7970c2c180bb761934 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -14,29 +14,9 @@ use bevy::{
 	window::WindowMode,
 };
 use bevy_atmosphere::prelude::*;
-//use bevy_easings::*;
-use bevy_egui::{egui, EguiContext, EguiPlugin};
-use bevy_mod_picking::{
-	events::Pointer,
-	prelude::*,
-	selection::{Deselect, Select},
-};
-use bevy_polyline::prelude::*;
-use chrono::{TimeZone, Utc};
 use clap::Parser;
-use dashmap::DashMap;
-use enterpolation::{Curve, Generator as _};
-use epaint::textures::TextureOptions;
-use num_traits::AsPrimitive;
-use palette::{convert::IntoColorUnclamped, IntoColor};
-use rand::distributions::Distribution;
-use rayon::iter::ParallelIterator;
-use sha2::Digest;
-use smooth_bevy_cameras::{
-	controllers::orbit::{OrbitCameraBundle, OrbitCameraController, OrbitCameraPlugin},
-	LookTransform, LookTransformPlugin,
-};
-use std::{fmt::Debug, io::Write, marker::PhantomData};
+use smooth_bevy_cameras::{controllers::orbit::{OrbitCameraBundle, OrbitCameraController, OrbitCameraPlugin}, LookTransformPlugin};
+use std::{fmt::Debug, marker::PhantomData};
 
 #[derive(Clone, Debug, Eq, Hash, PartialEq, SystemSet)]
 enum Set {
@@ -72,12 +52,20 @@ impl<
 		App::new()
 			.add_event::<state::BlockchainEvent<C>>()
 			.init_resource::<offchain::OffchainData<C>>()
+			.insert_resource(plugins::bc_reader::BcReaderRes {
+				reader: Box::new(bc_reader),
+				_p: Default::default(),
+			})
 			.add_plugins((
 				DefaultPlugins,
-				plugins::wotgraph::WotGraphPlugin::<C, 3>::default(),
-				plugins::bc_reader::BcReaderPlugin::new(Box::new(bc_reader)),
+				AtmospherePlugin,
+				LookTransformPlugin,
+				OrbitCameraPlugin::default(),
+				plugins::wotgraph::WotGraphPlugin::<C>::default(),
+				plugins::bc_reader::BcReaderPlugin::<C>::default(),
 			))
 			.add_systems(Startup, Self::setup)
+			.add_systems(Update, Self::keyboard_event_system)
 			.configure_sets(Update, Set::UpdatePositions)
 			.configure_sets(PreUpdate, Set::UpdateState)
 			.run();
@@ -94,9 +82,9 @@ impl<
 		commands
 			.spawn(OrbitCameraBundle::new(
 				OrbitCameraController::default(),
-				Vec3::new(0.0, -100.0, 0.0),
+				Vec3::new(-100.0, -100.0, -100.0),
 				Vec3::new(0.0, 0.0, 0.0),
-				Vec3::new(0.0, 0.0, 1.0),
+				Vec3::Y,
 			))
 			.insert(Camera3dBundle::default())
 			.insert(AtmosphereCamera::default());
@@ -105,6 +93,7 @@ impl<
 	fn keyboard_event_system(
 		mut keyboard_input_events: EventReader<KeyboardInput>,
 		mut windows: Query<&mut Window>,
+		mut bc_reader_cmd_event_writer: EventWriter<plugins::bc_reader::BcReaderCmdEvent>,
 	) {
 		for event in keyboard_input_events.read() {
 			match event {
@@ -118,21 +107,26 @@ impl<
 					state: ButtonState::Pressed,
 					..
 				} => {
-					if ui_state.target_block_number < bc_reader.available_blocks() - 1 {
-						ui_state.target_block_number += 1;
-					}
+					bc_reader_cmd_event_writer.send(plugins::bc_reader::BcReaderCmdEvent::ApplyOne);
 				}
 				KeyboardInput {
 					key_code: KeyCode::NumpadSubtract,
 					state: ButtonState::Pressed,
 					..
 				} => {
-					if ui_state.target_block_number > 0 {
-						ui_state.target_block_number -= 1;
-					}
+					bc_reader_cmd_event_writer
+						.send(plugins::bc_reader::BcReaderCmdEvent::RevertOne);
 				}
 				_ => {}
 			}
 		}
 	}
+
+	fn toggle_fullscreen(windows: &mut Query<&mut Window>) {
+		let mut window = windows.single_mut();
+		window.mode = match window.mode {
+			WindowMode::Windowed => WindowMode::Fullscreen,
+			_ => WindowMode::Windowed,
+		};
+	}
 }
diff --git a/src/plugins/bc_reader.rs b/src/plugins/bc_reader.rs
index ea2e8ff452a9a57a8784c0d9e1b4a18538bb5044..85e2a83ed4f2be8fb036e89947676d56842599bc 100644
--- a/src/plugins/bc_reader.rs
+++ b/src/plugins/bc_reader.rs
@@ -1,22 +1,66 @@
-use crate::{bc_reader, common::*};
+use crate::{
+	bc_reader::{self, BcReader as _},
+	common::*,
+};
 
 use bevy::prelude::*;
 use std::marker::PhantomData;
 
+#[derive(Resource)]
+pub struct BcReaderRes<C: blockchain::Config> {
+	pub reader: Box<dyn bc_reader::BcReader<C>>,
+	pub _p: PhantomData<C>,
+}
+
+#[derive(Event)]
+pub enum BcReaderCmdEvent {
+	ApplyOne,
+	RevertOne,
+}
+
+#[derive(Default)]
 pub struct BcReaderPlugin<C: blockchain::Config> {
-	reader: Box<dyn bc_reader::BcReader<C>>,
+	//reader: Box<dyn bc_reader::BcReader<C>>,
 	_p: PhantomData<C>,
 }
 
-impl<C: 'static + blockchain::Config + Send + Sync> BcReaderPlugin<C> {
-	pub fn new(reader: Box<dyn bc_reader::BcReader<C>>) -> Self {
-		Self {
-			reader,
-			_p: Default::default(),
-		}
+// impl<C: 'static + blockchain::Config + Send + Sync> BcReaderPlugin<C> {
+// 	pub fn new(reader: Box<dyn bc_reader::BcReader<C>>) -> Self {
+// 		Self {
+// 			reader,
+// 			_p: Default::default(),
+// 		}
+// 	}
+// }
+
+impl<C: 'static + blockchain::Config + Send + Sync> Plugin for BcReaderPlugin<C> {
+	fn build(&self, app: &mut App) {
+		app.add_event::<BcReaderCmdEvent>()
+			.add_systems(Update, cmd_event_listener::<C>);
 	}
 }
 
-impl<C: 'static + blockchain::Config + Send + Sync> Plugin for BcReaderPlugin<C> {
-	fn build(&self, app: &mut App) {}
+fn cmd_event_listener<C: 'static + blockchain::Config + Send + Sync>(
+	mut cmd_event_reader: EventReader<BcReaderCmdEvent>,
+	mut blockchain_event_writer: EventWriter<state::BlockchainEvent<C>>,
+	mut bc_reader: ResMut<BcReaderRes<C>>,
+) {
+	for event in cmd_event_reader.read() {
+		match event {
+			BcReaderCmdEvent::ApplyOne => {
+				if let Some(changeset) = bc_reader.as_mut().reader.apply_one() {
+					blockchain_event_writer.send(state::BlockchainEvent {
+						changeset
+					});
+				}
+			}
+			BcReaderCmdEvent::RevertOne => {
+				if let Some(changeset) = bc_reader.as_mut().reader.revert_one() {
+					blockchain_event_writer.send(state::BlockchainEvent {
+						changeset
+					});
+				}
+			}
+		}
+	}
 }
diff --git a/src/plugins/wotgraph.rs b/src/plugins/wotgraph.rs
index 7d0b01182394f1278e3a32d3c707a36c21e1c9db..25b55abfe27a1c78038d4cbf57eb6fbf6130cdcf 100644
--- a/src/plugins/wotgraph.rs
+++ b/src/plugins/wotgraph.rs
@@ -4,13 +4,14 @@ use bevy::prelude::*;
 use num_traits::Zero;
 use std::marker::PhantomData;
 
-pub type Layout<const N: usize> = forceatlas2::Layout<
-	f32,
-	3,
-	forceatlas2::EdgeBTreeMap<f32, IdtyId>,
-	forceatlas2::NodeHashMap<f32, 3, IdtyId>,
-	IdtyId,
->;
+#[derive(Default, Resource)]
+pub struct LayoutRes(pub forceatlas2::Layout<
+f32,
+3,
+forceatlas2::EdgeBTreeMap<f32, IdtyId>,
+forceatlas2::NodeHashMap<f32, 3, IdtyId>,
+IdtyId,
+>);
 
 #[derive(Bundle)]
 struct IdtyNodeBundle {
@@ -18,72 +19,82 @@ struct IdtyNodeBundle {
 	pbr: PbrBundle,
 }
 
-pub struct WotGraphPlugin<C, const N: usize> {
-	layout: Layout<N>,
+#[derive(Default)]
+pub struct WotGraphPlugin<C> {
 	_p: PhantomData<C>,
 }
 
-impl<C> Default for WotGraphPlugin<C, 3> {
-	fn default() -> Self {
-		Self {
-			layout: Layout::default(),
-			_p: Default::default(),
-		}
-	}
-}
-
-impl<C: 'static + blockchain::Config + Send + Sync, const N: usize> Plugin
-	for WotGraphPlugin<C, N>
+impl<C: 'static + blockchain::Config + Send + Sync> Plugin
+	for WotGraphPlugin<C>
 {
-	fn build(&self, app: &mut App) {}
+	fn build(&self, app: &mut App) {
+		app.insert_resource(LayoutRes::default())
+		.add_systems(Update, (Self::blockchain_event_listener, Self::iterate_layout, Self::update_positions));
+	}
 }
 
-impl<C: blockchain::Config, const N: usize> WotGraphPlugin<C, N> {
-	fn update_graph(
-		&mut self,
-		changeset: &state::Changeset<C>,
+impl<C: 'static + blockchain::Config> WotGraphPlugin<C> {
+	fn blockchain_event_listener(
+		mut blockchain_event_reader: EventReader<state::BlockchainEvent<C>>,
+		mut layout: ResMut<LayoutRes>,
 		mut commands: Commands,
 		mut meshes: ResMut<Assets<Mesh>>,
 		mut materials: ResMut<Assets<StandardMaterial>>,
 	) {
-		let mut rng = rand::thread_rng();
-		for cert in changeset.removed_certs.iter() {
-			let cert = cert.value();
-			self.layout.edges.remove(&(cert.issuer, cert.receiver));
-		}
-		for idty in changeset.removed_idties.iter() {
-			self.layout.nodes.remove(idty.key());
-		}
-		for idty in changeset.new_idties.iter() {
-			let pos = forceatlas2::sample_unit_cube(&mut rng);
-			self.layout.nodes.insert(
-				*idty.key(),
-				forceatlas2::Node {
-					pos,
-					speed: Zero::zero(),
-					old_speed: Zero::zero(),
-					size: 1.0,
-					mass: 1.0,
-				},
-			);
-			let mesh = meshes.add(Sphere::new(1.0).mesh().ico(3).unwrap());
-			commands.spawn(IdtyNodeBundle {
-				pbr: PbrBundle {
-					transform: Transform {
-						translation: pos.0.into(),
-						scale: Vec3::splat(1.0),
+		for event in blockchain_event_reader.read() {
+			let mut rng = rand::thread_rng();
+			for cert in event.changeset.removed_certs.iter() {
+				let cert = cert.value();
+				layout.0.edges.remove(&(cert.issuer, cert.receiver));
+			}
+			for idty in event.changeset.removed_idties.iter() {
+				layout.0.nodes.remove(idty.key());
+			}
+			for idty in event.changeset.new_idties.iter() {
+				let pos = forceatlas2::sample_unit_cube(&mut rng);
+				layout.0.nodes.insert(
+					*idty.key(),
+					forceatlas2::Node {
+						pos,
+						speed: Zero::zero(),
+						old_speed: Zero::zero(),
+						size: 1.0,
+						mass: 1.0,
+					},
+				);
+				let mesh = meshes.add(Sphere::new(1.0).mesh().ico(3).unwrap());
+				commands.spawn(IdtyNodeBundle {
+					pbr: PbrBundle {
+						transform: Transform {
+							translation: pos.0.into(),
+							scale: Vec3::splat(1.0),
+							..default()
+						},
+						mesh: mesh.clone(),
+						material: materials.add(Color::rgb(1.0, 1.0, 1.0)),
 						..default()
 					},
-					mesh: mesh.clone(),
-					material: materials.add(Color::rgb(1.0, 1.0, 1.0)),
-					..default()
-				},
-				idty_id: *idty.key(),
-			});
-		}
-		for cert in changeset.new_certs.iter() {
-			let cert = cert.value();
-			self.layout.edges.insert((cert.issuer, cert.receiver), 1.0);
+					idty_id: *idty.key(),
+				});
+			}
+			for cert in event.changeset.new_certs.iter() {
+				let cert = cert.value();
+				layout.0.edges.insert((cert.issuer, cert.receiver), 1.0);
+			}
 		}
 	}
+
+	fn iterate_layout(
+		mut layout: ResMut<LayoutRes>,) {
+		layout.0.iteration();
+	}
+
+	fn update_positions(
+		mut layout: ResMut<LayoutRes>,
+		mut query_idty: Query<(&IdtyId, &mut Transform)>,
+	) {
+			for (idty_id, mut idty_pos) in query_idty.iter_mut() {
+				idty_pos.translation = layout.0.nodes[idty_id].pos.0.into();
+			}
+	}
 }