diff --git a/src/input.rs b/src/input.rs
index cff8084..522b569 100644
--- a/src/input.rs
+++ b/src/input.rs
@@ -16,6 +16,7 @@ pub struct Inputs {
 #[derive(Unique, Clone, Default, Debug)]
 pub struct RawInputState {
   pub keyboard_state: HashSet<VirtualKeyCode, BuildNoHashHasher<u32>>,
+  pub button_state: [bool; 32],
   pub mouse_delta: DVec2
 }
 
@@ -37,8 +38,10 @@ pub fn process_events(
           };
         }
       },
-      DeviceEvent::Button { button: _, state: _ } => {
-        //log::debug!("Button {button} {state:?}");
+      DeviceEvent::Button { button, state } => {
+        if button < 32 {
+          input_state.button_state[button as usize] = matches!(state, ElementState::Pressed);
+        }
       },
       _ => ()
     }
@@ -56,6 +59,8 @@ pub fn update_input_states (
     raw_inputs.keyboard_state.contains(&VirtualKeyCode::S) as u32 as f32
   ).normalize_or_zero();
   inputs.look = raw_inputs.mouse_delta.as_vec2();
+  inputs.action_a = raw_inputs.button_state[1];
+  inputs.action_b = raw_inputs.button_state[3];
 }
 
 pub fn init_input (
diff --git a/src/main.rs b/src/main.rs
index e4cec44..aa72591 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -13,6 +13,7 @@ use glam::vec3;
 use std::time::{Instant, Duration};
 
 mod logging;
+
 pub(crate) mod rendering;
 pub(crate) mod world;
 pub(crate) mod player;
@@ -24,8 +25,17 @@ pub(crate) mod events;
 pub(crate) mod input;
 pub(crate) mod fly_controller;
 
-use rendering::{Renderer, RenderTarget, BackgroundColor, clear_background};
-use world::{loading::update_loaded_world_around_player, render::draw_world, init_game_world};
+use rendering::{
+  Renderer, 
+  RenderTarget, 
+  BackgroundColor, 
+  clear_background
+};
+use world::{
+  init_game_world,
+  loading::update_loaded_world_around_player, 
+  raycast::update_player_raycast
+};
 use player::spawn_player;
 use prefabs::load_prefabs;
 use settings::GameSettings;
@@ -33,6 +43,10 @@ use camera::compute_cameras;
 use events::{clear_events, process_glutin_events};
 use input::{init_input, process_inputs};
 use fly_controller::update_controllers;
+use rendering::{
+  selection_box::render_selection_box,
+  world::draw_world,
+};
 
 #[derive(Unique)]
 pub(crate) struct DeltaTime(Duration);
@@ -50,6 +64,7 @@ fn update() -> Workload {
     process_inputs,
     update_controllers,
     update_loaded_world_around_player,
+    update_player_raycast,
     compute_cameras
   ).into_workload()
 }
diff --git a/src/player.rs b/src/player.rs
index 88ce652..4a2fbc1 100644
--- a/src/player.rs
+++ b/src/player.rs
@@ -1,14 +1,12 @@
 use glam::Mat4;
-use shipyard::{Component, EntitiesViewMut, ViewMut};
+use shipyard::{Component, AllStoragesViewMut};
 use crate::{
   transform::Transform,
   camera::Camera, 
-  fly_controller::FlyController,
+  fly_controller::FlyController, 
+  world::raycast::LookingAtBlock,
 };
 
-#[derive(Component)]
-pub struct LocalPlayer;
-
 #[derive(Component)]
 pub struct Player;
 
@@ -16,31 +14,15 @@ pub struct Player;
 pub struct MainPlayer;
 
 pub fn spawn_player (
-  mut entities: EntitiesViewMut,
-  mut vm_player: ViewMut<Player>,
-  mut vm_main_player: ViewMut<MainPlayer>,
-  mut vm_local_player: ViewMut<LocalPlayer>,
-  mut vm_transform: ViewMut<Transform>,
-  mut vm_camera: ViewMut<Camera>,
-  mut vm_controls: ViewMut<FlyController>,
+  mut storages: AllStoragesViewMut
 ) {
   log::info!("spawning player");
-  entities.add_entity(
-    (
-      &mut vm_player,
-      &mut vm_main_player,
-      &mut vm_local_player,
-      &mut vm_transform,
-      &mut vm_camera,
-      &mut vm_controls
-    ),
-    (
-      Player,
-      MainPlayer,
-      LocalPlayer,
-      Transform(Mat4::default()),
-      Camera::default(),
-      FlyController
-    )
-  );
+  storages.add_entity((
+    Player,
+    MainPlayer,
+    Transform::default(),
+    Camera::default(),
+    FlyController,
+    LookingAtBlock::default(),
+  ));
 }
diff --git a/src/rendering.rs b/src/rendering.rs
index 2bf0517..7a42932 100644
--- a/src/rendering.rs
+++ b/src/rendering.rs
@@ -9,6 +9,9 @@ use glium::{
 };
 use glam::Vec3;
 
+pub mod world;
+pub mod selection_box;
+
 #[derive(Unique)]
 pub struct RenderTarget(pub glium::Frame);
 
diff --git a/src/rendering/selection_box.rs b/src/rendering/selection_box.rs
new file mode 100644
index 0000000..4871bf2
--- /dev/null
+++ b/src/rendering/selection_box.rs
@@ -0,0 +1,18 @@
+use shipyard::{View, IntoIter, NonSendSync, UniqueViewMut};
+use crate::{
+  player::MainPlayer, 
+  world::raycast::LookingAtBlock, 
+  camera::Camera
+};
+use super::RenderTarget;
+
+//wip
+pub fn render_selection_box(
+  lookat: View<LookingAtBlock>,
+  camera: View<Camera>,
+  mut target: NonSendSync<UniqueViewMut<RenderTarget>>, 
+) {
+  for lookat in lookat.iter() {
+    
+  }
+}
diff --git a/src/world/render.rs b/src/rendering/world.rs
similarity index 99%
rename from src/world/render.rs
rename to src/rendering/world.rs
index 3cda8fc..26f8822 100644
--- a/src/world/render.rs
+++ b/src/rendering/world.rs
@@ -19,7 +19,6 @@ use glium::{
 };
 use crate::{
   camera::Camera, 
-  rendering::RenderTarget,
   prefabs::{
     ChunkShaderPrefab,
     BlockTexturesPrefab,
@@ -30,6 +29,7 @@ use crate::{
     chunk::CHUNK_SIZE,
   }, 
 };
+use super::RenderTarget;
 
 #[derive(Clone, Copy)]
 pub struct ChunkVertex {
diff --git a/src/world.rs b/src/world.rs
index 2f3ace1..a52c55a 100644
--- a/src/world.rs
+++ b/src/world.rs
@@ -6,16 +6,18 @@ use anyhow::{Result, Context};
 
 pub mod chunk;
 pub mod block;
-pub mod render;
 pub mod tasks;
 pub mod loading;
 pub mod mesh;
 pub mod neighbors;
 pub mod worldgen;
+pub mod raycast;
 
 use chunk::{Chunk, ChunkMesh};
 use tasks::ChunkTaskManager;
 
+use self::{chunk::CHUNK_SIZE, block::Block};
+
 //TODO separate world struct for render data
 // because this is not send-sync
 
@@ -25,6 +27,30 @@ pub struct ChunkStorage {
   pub chunks: HashMap<IVec3, Chunk>
 }
 impl ChunkStorage {
+  pub const fn to_chunk_coords(position: IVec3) -> (IVec3, IVec3) {
+    (
+      IVec3::new(
+        position.x.div_euclid(CHUNK_SIZE as i32),
+        position.y.div_euclid(CHUNK_SIZE as i32),
+        position.z.div_euclid(CHUNK_SIZE as i32),
+      ),
+      IVec3::new(
+        position.x.rem_euclid(CHUNK_SIZE as i32),
+        position.y.rem_euclid(CHUNK_SIZE as i32),
+        position.z.rem_euclid(CHUNK_SIZE as i32),
+      )
+    )
+  }
+  pub fn get_block(&self, position: IVec3) -> Option<Block> {
+    let (chunk, block) = Self::to_chunk_coords(position);
+    let block = self.chunks
+      .get(&chunk)?
+      .block_data.as_ref()?
+      .blocks.get(block.x as usize)?
+      .get(block.y as usize)?
+      .get(block.z as usize)?;
+    Some(*block)
+  }
   pub fn new() -> Self {
     Self::default()
   }
diff --git a/src/world/chunk.rs b/src/world/chunk.rs
index 20e91e5..ba46819 100644
--- a/src/world/chunk.rs
+++ b/src/world/chunk.rs
@@ -1,6 +1,7 @@
 use glam::IVec3;
 use glium::{VertexBuffer, IndexBuffer};
-use super::{block::Block, render::ChunkVertex};
+use super::block::Block;
+use crate::rendering::world::ChunkVertex;
 
 pub const CHUNK_SIZE: usize = 32;
 
diff --git a/src/world/loading.rs b/src/world/loading.rs
index 226b6e3..498c3af 100644
--- a/src/world/loading.rs
+++ b/src/world/loading.rs
@@ -2,7 +2,7 @@ use glam::{IVec3, ivec3};
 use glium::{VertexBuffer, IndexBuffer, index::PrimitiveType};
 use shipyard::{View, UniqueView, UniqueViewMut, IntoIter, Workload, IntoWorkload, NonSendSync};
 use crate::{
-  player::LocalPlayer,
+  player::MainPlayer,
   transform::Transform,
   settings::GameSettings,
   rendering::Renderer
@@ -27,7 +27,7 @@ pub fn update_loaded_world_around_player() -> Workload {
 
 pub fn update_chunks_if_player_moved(
   v_settings: UniqueView<GameSettings>,
-  v_local_player: View<LocalPlayer>,
+  v_local_player: View<MainPlayer>,
   v_transform: View<Transform>,
   mut vm_world: UniqueViewMut<ChunkStorage>,
 ) {
diff --git a/src/world/mesh.rs b/src/world/mesh.rs
index 6d6b6f2..69018dc 100644
--- a/src/world/mesh.rs
+++ b/src/world/mesh.rs
@@ -1,6 +1,7 @@
 use strum::{EnumIter, IntoEnumIterator};
 use glam::{Vec3A, vec3a, IVec3, ivec3};
-use super::{render::ChunkVertex, chunk::CHUNK_SIZE, block::{Block, RenderType}};
+use super::{chunk::CHUNK_SIZE, block::{Block, RenderType}};
+use crate::rendering::world::ChunkVertex;
 
 pub mod data;
 use data::MeshGenData;
diff --git a/src/world/raycast.rs b/src/world/raycast.rs
new file mode 100644
index 0000000..303df3e
--- /dev/null
+++ b/src/world/raycast.rs
@@ -0,0 +1,55 @@
+use glam::{Vec3, IVec3};
+use shipyard::{View, Component, ViewMut, IntoIter, UniqueView};
+use crate::{player::MainPlayer, transform::Transform};
+
+use super::{ChunkStorage, block::Block};
+
+const RAYCAST_STEP: f32 = 0.25;
+
+#[derive(Clone, Copy, Debug)]
+pub struct RaycastReport {
+  pub length: f32,
+  pub position: Vec3,
+  pub block_position: IVec3,
+  pub block: Block,
+}
+
+impl ChunkStorage {
+  //this is probably pretty slow...
+  pub fn raycast(&self, origin: Vec3, direction: Vec3, limit: Option<f32>) -> Option<RaycastReport> {
+    debug_assert!(direction.is_normalized(), "Ray direction not normalized");
+    let mut position = origin;
+    let mut length = 0.;
+    loop {
+      let block_position = position.floor().as_ivec3();
+      if let Some(block) = self.get_block(block_position) {
+        if block.descriptor().raycast_collision {
+          return Some(RaycastReport { length, position, block_position, block });
+        }
+      }
+      length += RAYCAST_STEP;
+      position += direction * RAYCAST_STEP;
+      if let Some(limit) = limit {
+        if length > limit {
+          return None;
+        }
+      }
+    }
+  }
+}
+
+#[derive(Component, Clone, Copy, Debug, Default)]
+pub struct LookingAtBlock(pub Option<RaycastReport>);
+
+pub fn update_player_raycast(
+  main_player: View<MainPlayer>,
+  transform: View<Transform>,
+  mut raycast: ViewMut<LookingAtBlock>,
+  world: UniqueView<ChunkStorage>,
+) {
+  for (_, transform, report) in (&main_player, transform.inserted_or_modified(), &mut raycast).iter() {
+    let (_, rotation, position) = transform.0.to_scale_rotation_translation();
+    let direction = rotation * Vec3::NEG_Z;
+    *report = LookingAtBlock(world.raycast(position, direction, Some(10.)));
+  }
+}
diff --git a/src/world/tasks.rs b/src/world/tasks.rs
index 61a1c8d..2c73c16 100644
--- a/src/world/tasks.rs
+++ b/src/world/tasks.rs
@@ -4,10 +4,10 @@ use shipyard::Unique;
 use rayon::{ThreadPool, ThreadPoolBuilder};
 use super::{
   chunk::BlockData,
-  render::ChunkVertex, 
   mesh::{generate_mesh, data::MeshGenData},
   worldgen::generate_world,
 };
+use crate::rendering::world::ChunkVertex;
 
 pub enum ChunkTask {
   LoadChunk {