diff --git a/Cargo.lock b/Cargo.lock
index a0c9e2c6d82e95217f8fcc033d15b31214da810c..f13ccecd9e29def568afc4ffb82397ee86fe0dd9 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -310,8 +310,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7baf73c58d41c353c6fd08e6764a2e7420c9f19e8227b391c50981db6d0282a6"
 dependencies = [
  "bevy_macro_utils",
- "quote",
- "syn",
+ "quote 1.0.23",
+ "syn 1.0.107",
 ]
 
 [[package]]
@@ -355,9 +355,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c15bd45438eeb681ad74f2d205bb07a5699f98f9524462a30ec764afab2742ce"
 dependencies = [
  "bevy_macro_utils",
- "proc-macro2",
- "quote",
- "syn",
+ "proc-macro2 1.0.49",
+ "quote 1.0.23",
+ "syn 1.0.107",
 ]
 
 [[package]]
@@ -501,8 +501,8 @@ version = "0.9.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "022bb69196deeea691b6997414af85bbd7f2b34a8914c4aa7a7ff4dfa44f7677"
 dependencies = [
- "quote",
- "syn",
+ "quote 1.0.23",
+ "syn 1.0.107",
  "toml",
 ]
 
@@ -581,9 +581,9 @@ checksum = "a2bf4cb9cd5acb4193f890f36cb63679f1502e2de025e66a63b194b8b133d018"
 dependencies = [
  "bevy_macro_utils",
  "bit-set",
- "proc-macro2",
- "quote",
- "syn",
+ "proc-macro2 1.0.49",
+ "quote 1.0.23",
+ "syn 1.0.107",
  "uuid",
 ]
 
@@ -636,9 +636,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b7acae697776ac05bea523e1725cf2660c91c53abe72c66782ea1e1b9eedb572"
 dependencies = [
  "bevy_macro_utils",
- "proc-macro2",
- "quote",
- "syn",
+ "proc-macro2 1.0.49",
+ "quote 1.0.23",
+ "syn 1.0.107",
 ]
 
 [[package]]
@@ -842,12 +842,12 @@ dependencies = [
  "lazy_static",
  "lazycell",
  "peeking_take_while",
- "proc-macro2",
- "quote",
+ "proc-macro2 1.0.49",
+ "quote 1.0.23",
  "regex",
  "rustc-hash",
  "shlex",
- "syn",
+ "syn 1.0.107",
 ]
 
 [[package]]
@@ -898,9 +898,9 @@ version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5fe233b960f12f8007e3db2d136e3cb1c291bfd7396e384ee76025fc1a3932b4"
 dependencies = [
- "proc-macro2",
- "quote",
- "syn",
+ "proc-macro2 1.0.49",
+ "quote 1.0.23",
+ "syn 1.0.107",
 ]
 
 [[package]]
@@ -1189,14 +1189,38 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "darling"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcfbcb0c5961907597a7d1148e3af036268f2b773886b8bb3eeb1e1281d3d3d6"
+dependencies = [
+ "darling_core 0.9.0",
+ "darling_macro 0.9.0",
+]
+
 [[package]]
 name = "darling"
 version = "0.13.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c"
 dependencies = [
- "darling_core",
- "darling_macro",
+ "darling_core 0.13.4",
+ "darling_macro 0.13.4",
+]
+
+[[package]]
+name = "darling_core"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6afc018370c3bff3eb51f89256a6bdb18b4fdcda72d577982a14954a7a0b402c"
+dependencies = [
+ "fnv",
+ "ident_case",
+ "proc-macro2 0.4.30",
+ "quote 0.6.13",
+ "strsim 0.7.0",
+ "syn 0.15.44",
 ]
 
 [[package]]
@@ -1207,10 +1231,21 @@ checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610"
 dependencies = [
  "fnv",
  "ident_case",
- "proc-macro2",
- "quote",
- "strsim",
- "syn",
+ "proc-macro2 1.0.49",
+ "quote 1.0.23",
+ "strsim 0.10.0",
+ "syn 1.0.107",
+]
+
+[[package]]
+name = "darling_macro"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6d8dac1c6f1d29a41c4712b4400f878cb4fcc4c7628f298dd75038e024998d1"
+dependencies = [
+ "darling_core 0.9.0",
+ "quote 0.6.13",
+ "syn 0.15.44",
 ]
 
 [[package]]
@@ -1219,9 +1254,34 @@ version = "0.13.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
 dependencies = [
- "darling_core",
- "quote",
- "syn",
+ "darling_core 0.13.4",
+ "quote 1.0.23",
+ "syn 1.0.107",
+]
+
+[[package]]
+name = "derive_builder"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ac53fa6a3cda160df823a9346442525dcaf1e171999a1cf23e67067e4fd64d4"
+dependencies = [
+ "darling 0.9.0",
+ "derive_builder_core",
+ "proc-macro2 0.4.30",
+ "quote 0.6.13",
+ "syn 0.15.44",
+]
+
+[[package]]
+name = "derive_builder_core"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0288a23da9333c246bb18c143426074a6ae96747995c5819d2947b64cd942b37"
+dependencies = [
+ "darling 0.9.0",
+ "proc-macro2 0.4.30",
+ "quote 0.6.13",
+ "syn 0.15.44",
 ]
 
 [[package]]
@@ -1269,9 +1329,9 @@ version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ec27b639e942eb0297513b81cc6d87c50f6c77dc8c37af00a39ed5db3b9657ee"
 dependencies = [
- "proc-macro2",
- "quote",
- "syn",
+ "proc-macro2 1.0.49",
+ "quote 1.0.23",
+ "syn 1.0.107",
 ]
 
 [[package]]
@@ -1503,9 +1563,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bdd53d6e284bb2bf02a6926e4cc4984978c1990914d6cd9deae4e31cf37cd113"
 dependencies = [
  "inflections",
- "proc-macro2",
- "quote",
- "syn",
+ "proc-macro2 1.0.49",
+ "quote 1.0.23",
+ "syn 1.0.107",
 ]
 
 [[package]]
@@ -1595,9 +1655,9 @@ version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "59d2aba832b60be25c1b169146b27c64115470981b128ed84c8db18c1b03c6ff"
 dependencies = [
- "proc-macro2",
- "quote",
- "syn",
+ "proc-macro2 1.0.49",
+ "quote 1.0.23",
+ "syn 1.0.107",
 ]
 
 [[package]]
@@ -1647,7 +1707,7 @@ dependencies = [
  "bytemuck",
  "byteorder",
  "color_quant",
- "num-rational",
+ "num-rational 0.4.1",
  "num-traits",
  "png",
  "scoped_threadpool",
@@ -1922,13 +1982,14 @@ dependencies = [
 
 [[package]]
 name = "micro_ldtk"
-version = "0.1.0"
+version = "0.2.0"
 dependencies = [
  "anyhow",
  "bevy",
  "ldtk_rust",
  "log",
  "num-traits",
+ "quadtree_rs",
  "serde",
  "serde_json",
 ]
@@ -1979,7 +2040,7 @@ dependencies = [
  "spirv",
  "termcolor",
  "thiserror",
- "unicode-xid",
+ "unicode-xid 0.2.4",
 ]
 
 [[package]]
@@ -2038,11 +2099,11 @@ version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0df7ac00c4672f9d5aece54ee3347520b7e20f158656c7db2e6de01902eb7a6c"
 dependencies = [
- "darling",
+ "darling 0.13.4",
  "proc-macro-crate",
- "proc-macro2",
- "quote",
- "syn",
+ "proc-macro2 1.0.49",
+ "quote 1.0.23",
+ "syn 1.0.107",
 ]
 
 [[package]]
@@ -2126,15 +2187,50 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "num"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8536030f9fea7127f841b45bb6243b27255787fb4eb83958aa1ef9d2fdc0c36"
+dependencies = [
+ "num-bigint",
+ "num-complex",
+ "num-integer",
+ "num-iter",
+ "num-rational 0.2.4",
+ "num-traits",
+]
+
+[[package]]
+name = "num-bigint"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304"
+dependencies = [
+ "autocfg",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-complex"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95"
+dependencies = [
+ "autocfg",
+ "num-traits",
+]
+
 [[package]]
 name = "num-derive"
 version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d"
 dependencies = [
- "proc-macro2",
- "quote",
- "syn",
+ "proc-macro2 1.0.49",
+ "quote 1.0.23",
+ "syn 1.0.107",
 ]
 
 [[package]]
@@ -2147,6 +2243,29 @@ dependencies = [
  "num-traits",
 ]
 
+[[package]]
+name = "num-iter"
+version = "0.1.43"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
+dependencies = [
+ "autocfg",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-rational"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef"
+dependencies = [
+ "autocfg",
+ "num-bigint",
+ "num-integer",
+ "num-traits",
+]
+
 [[package]]
 name = "num-rational"
 version = "0.4.1"
@@ -2183,9 +2302,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce"
 dependencies = [
  "proc-macro-crate",
- "proc-macro2",
- "quote",
- "syn",
+ "proc-macro2 1.0.49",
+ "quote 1.0.23",
+ "syn 1.0.107",
 ]
 
 [[package]]
@@ -2341,7 +2460,7 @@ version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bb458bb7f6e250e6eb79d5026badc10a3ebb8f9a15d1fff0f13d17c71f4d6dee"
 dependencies = [
- "unicode-xid",
+ "unicode-xid 0.2.4",
 ]
 
 [[package]]
@@ -2355,6 +2474,15 @@ dependencies = [
  "toml",
 ]
 
+[[package]]
+name = "proc-macro2"
+version = "0.4.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
+dependencies = [
+ "unicode-xid 0.1.0",
+]
+
 [[package]]
 name = "proc-macro2"
 version = "1.0.49"
@@ -2370,13 +2498,32 @@ version = "1.0.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "74605f360ce573babfe43964cbe520294dcb081afbf8c108fc6e23036b4da2df"
 
+[[package]]
+name = "quadtree_rs"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ec1506903c0f5cff273bd34df6646c7040f2a7c5b0a92a9179d89551bed9ffd"
+dependencies = [
+ "derive_builder",
+ "num",
+]
+
+[[package]]
+name = "quote"
+version = "0.6.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1"
+dependencies = [
+ "proc-macro2 0.4.30",
+]
+
 [[package]]
 name = "quote"
 version = "1.0.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
 dependencies = [
- "proc-macro2",
+ "proc-macro2 1.0.49",
 ]
 
 [[package]]
@@ -2549,9 +2696,9 @@ version = "1.0.152"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
 dependencies = [
- "proc-macro2",
- "quote",
- "syn",
+ "proc-macro2 1.0.49",
+ "quote 1.0.23",
+ "syn 1.0.107",
 ]
 
 [[package]]
@@ -2658,11 +2805,11 @@ version = "0.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef"
 dependencies = [
- "proc-macro2",
- "quote",
+ "proc-macro2 1.0.49",
+ "quote 1.0.23",
  "serde",
  "serde_derive",
- "syn",
+ "syn 1.0.107",
 ]
 
 [[package]]
@@ -2672,13 +2819,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11"
 dependencies = [
  "base-x",
- "proc-macro2",
- "quote",
+ "proc-macro2 1.0.49",
+ "quote 1.0.23",
  "serde",
  "serde_derive",
  "serde_json",
  "sha1",
- "syn",
+ "syn 1.0.107",
 ]
 
 [[package]]
@@ -2687,6 +2834,12 @@ version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0"
 
+[[package]]
+name = "strsim"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
+
 [[package]]
 name = "strsim"
 version = "0.10.0"
@@ -2699,14 +2852,25 @@ version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8fb1df15f412ee2e9dfc1c504260fa695c1c3f10fe9f4a6ee2d2184d7d6450e2"
 
+[[package]]
+name = "syn"
+version = "0.15.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5"
+dependencies = [
+ "proc-macro2 0.4.30",
+ "quote 0.6.13",
+ "unicode-xid 0.1.0",
+]
+
 [[package]]
 name = "syn"
 version = "1.0.107"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
 dependencies = [
- "proc-macro2",
- "quote",
+ "proc-macro2 1.0.49",
+ "quote 1.0.23",
  "unicode-ident",
 ]
 
@@ -2747,9 +2911,9 @@ version = "1.0.38"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
 dependencies = [
- "proc-macro2",
- "quote",
- "syn",
+ "proc-macro2 1.0.49",
+ "quote 1.0.23",
+ "syn 1.0.107",
 ]
 
 [[package]]
@@ -2803,9 +2967,9 @@ version = "0.1.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a"
 dependencies = [
- "proc-macro2",
- "quote",
- "syn",
+ "proc-macro2 1.0.49",
+ "quote 1.0.23",
+ "syn 1.0.107",
 ]
 
 [[package]]
@@ -2882,6 +3046,12 @@ version = "0.1.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
 
+[[package]]
+name = "unicode-xid"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
+
 [[package]]
 name = "unicode-xid"
 version = "0.2.4"
@@ -2958,9 +3128,9 @@ dependencies = [
  "bumpalo",
  "log",
  "once_cell",
- "proc-macro2",
- "quote",
- "syn",
+ "proc-macro2 1.0.49",
+ "quote 1.0.23",
+ "syn 1.0.107",
  "wasm-bindgen-shared",
 ]
 
@@ -2982,7 +3152,7 @@ version = "0.2.83"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
 dependencies = [
- "quote",
+ "quote 1.0.23",
  "wasm-bindgen-macro-support",
 ]
 
@@ -2992,9 +3162,9 @@ version = "0.2.83"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
 dependencies = [
- "proc-macro2",
- "quote",
- "syn",
+ "proc-macro2 1.0.49",
+ "quote 1.0.23",
+ "syn 1.0.107",
  "wasm-bindgen-backend",
  "wasm-bindgen-shared",
 ]
diff --git a/Cargo.toml b/Cargo.toml
index 155f24ea5ec6b4e203f590c8d363680a3b333676..45d190d8919d200659b838873cce8e721db05dc0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -17,4 +17,5 @@ serde = "1.0.147"
 serde_json = "1.0.87"
 ldtk_rust = "0.6.0"
 num-traits = "0.2.15"
+quadtree_rs = "0.1.2"
 
diff --git a/src/assets.rs b/src/assets.rs
index b9101ef85a385b74d46186638458f4889c0e8787..8e450e2185758a2da2cb54aa080877b3b80daa7f 100644
--- a/src/assets.rs
+++ b/src/assets.rs
@@ -3,13 +3,13 @@ use std::ops::{Deref, DerefMut};
 
 use anyhow::Error;
 use bevy::asset::{AssetEvent, AssetLoader, Assets, BoxedFuture, LoadContext, LoadedAsset};
-use bevy::prelude::{EventReader, Res, ResMut, Resource};
+use bevy::prelude::{EventReader, EventWriter, Res, ResMut, Resource};
 use bevy::reflect::TypeUuid;
 use ldtk_rust::Project;
 use serde_json::Value;
 
 use crate::utils::SerdeClone;
-use crate::LdtkLevel;
+use crate::{LdtkLevel, LevelDataUpdated};
 
 #[derive(TypeUuid)]
 #[uuid = "292a8918-9487-11ed-8dd2-43b6f36cb076"]
@@ -87,6 +87,7 @@ pub fn handle_ldtk_project_events(
 	assets: Res<Assets<LdtkProject>>,
 	mut level_index: ResMut<LevelIndex>,
 	mut tilset_index: ResMut<TilesetIndex>,
+	mut update_events: EventWriter<LevelDataUpdated>,
 ) {
 	for event in events.iter() {
 		match event {
@@ -97,6 +98,7 @@ pub fn handle_ldtk_project_events(
 							level.identifier.clone(),
 							LdtkLevel::from(level.serde_clone()),
 						);
+						update_events.send(LevelDataUpdated(level.identifier.clone()));
 					}
 
 					for tileset in &project.defs.tilesets {
diff --git a/src/lib.rs b/src/lib.rs
index 53bb4f2105a04f77c6ac322ec295e9d1903d8c74..c280b1b71e5c8f4e1ac164f81a2f3b1a7caa9590 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -2,6 +2,7 @@ mod assets;
 mod camera;
 mod locator;
 mod map_query;
+mod pregen;
 mod types;
 pub(crate) mod utils;
 // mod spawning;
@@ -28,10 +29,13 @@ mod __plugin {
 	use bevy::asset::AddAsset;
 	use bevy::ecs::query::ReadOnlyWorldQuery;
 
+	use crate::LevelDataUpdated;
+
 	pub struct MicroLDTKPlugin;
 	impl Plugin for MicroLDTKPlugin {
 		fn build(&self, app: &mut App) {
-			app.init_resource::<super::assets::TilesetIndex>()
+			app.add_event::<LevelDataUpdated>()
+				.init_resource::<super::assets::TilesetIndex>()
 				.init_resource::<super::assets::LevelIndex>()
 				.add_asset::<super::assets::LdtkProject>()
 				.add_asset_loader(super::assets::LdtkLoader)
@@ -75,5 +79,8 @@ pub use __plugin::{MicroLDTKCameraPlugin, MicroLDTKPlugin};
 pub use assets::{LdtkLoader, LdtkProject, LevelIndex, TileMetadata, TilesetIndex};
 pub use camera::CameraBounder;
 pub use map_query::{CameraBounds, MapQuery};
-pub use types::{LdtkLayer, LdtkLevel, SpatialIndex, TileFlip, TileRef};
-pub use utils::{entity_centre, grid_to_px, px_to_grid, ActiveLevel, WorldLinked};
+pub use pregen::{
+	write_layer_to_texture, write_map_to_texture, Rasterise, SuppliesImage, SuppliesTextureAtlas,
+};
+pub use types::{LdtkLayer, LdtkLevel, LevelDataUpdated, SpatialIndex, TileFlip, TileRef};
+pub use utils::{entity_centre, grid_to_px, px_to_grid, ActiveLevel, Indexer, WorldLinked};
diff --git a/src/map_query.rs b/src/map_query.rs
index 29afdd83d5e5722cc984da1ad4e13d8743357130..43c62430623864616e01f0f583b63c70727d32c7 100644
--- a/src/map_query.rs
+++ b/src/map_query.rs
@@ -23,6 +23,19 @@ pub struct InstanceRef<'a> {
 }
 
 impl<'a> InstanceRef<'a> {
+	pub fn x(&self) -> i64 {
+		self.entity.px[0]
+	}
+	pub fn y(&self) -> i64 {
+		self.entity.px[1]
+	}
+	pub fn width(&self) -> i64 {
+		self.entity.width
+	}
+	pub fn height(&self) -> i64 {
+		self.entity.height
+	}
+
 	pub fn get_type(&self) -> &'a String {
 		&self.entity.identifier
 	}
@@ -55,16 +68,16 @@ pub struct CameraBounds {
 
 impl CameraBounds {
 	pub fn get_min_x(&self, camera_width: f32) -> f32 {
-		self.left + (camera_width / 2.0) - (get_ldtk_tile_scale() / 2.0)
+		self.left + (camera_width / 2.0) // - (get_ldtk_tile_scale() / 2.0)
 	}
 	pub fn get_max_x(&self, camera_width: f32) -> f32 {
-		self.right - (camera_width / 2.0) - (get_ldtk_tile_scale() / 2.0)
+		self.right - (camera_width / 2.0) // - (get_ldtk_tile_scale() / 2.0)
 	}
 	pub fn get_min_y(&self, camera_height: f32) -> f32 {
-		self.bottom + (camera_height / 2.0) - (get_ldtk_tile_scale() / 2.0)
+		self.bottom + (camera_height / 2.0) // - (get_ldtk_tile_scale() / 2.0)
 	}
 	pub fn get_max_y(&self, camera_height: f32) -> f32 {
-		self.top - (camera_height / 2.0) - (get_ldtk_tile_scale() / 2.0)
+		self.top - (camera_height / 2.0) // - (get_ldtk_tile_scale() / 2.0)
 	}
 }
 
@@ -79,6 +92,10 @@ impl<'w, 's> MapQuery<'w, 's> {
 		}
 	}
 
+	pub fn get_layers_of(level: &LdtkLevel) -> impl DoubleEndedIterator<Item = &LdtkLayer> {
+		level.layers()
+	}
+
 	pub fn get_entities_of(level: &LdtkLevel) -> Vec<&EntityInstance> {
 		level
 			.layers()
@@ -111,6 +128,24 @@ impl<'w, 's> MapQuery<'w, 's> {
 			.collect()
 	}
 
+	pub fn get_filtered_instance_refs_of(
+		level: &LdtkLevel,
+		entity_type: impl ToString,
+	) -> Vec<InstanceRef> {
+		let e_type = entity_type.to_string();
+		level
+			.layers()
+			.flat_map(|layer| {
+				layer
+					.as_ref()
+					.entity_instances
+					.iter()
+					.map(|inst| InstanceRef { entity: inst })
+			})
+			.filter(|inst| inst.entity.identifier == e_type)
+			.collect()
+	}
+
 	pub fn get_owned_entities_of(level: &LdtkLevel) -> Vec<EntityInstance> {
 		level
 			.layers()
@@ -134,10 +169,20 @@ impl<'w, 's> MapQuery<'w, 's> {
 		}
 	}
 
-	pub fn get_active_level(&self) -> Option<&LdtkLevel> {
+	pub fn active_level_is(&self, name: impl ToString) -> bool {
 		self.active
 			.as_ref()
-			.and_then(|index| self.index.get(&index.map))
+			.map(|active_level| active_level.map == name.to_string())
+			.unwrap_or(false)
+	}
+
+	pub fn get_active_level_name(&self) -> Option<&String> {
+		self.active.as_ref().map(|m| &m.map)
+	}
+
+	pub fn get_active_level(&self) -> Option<&LdtkLevel> {
+		self.get_active_level_name()
+			.and_then(|index| self.index.get(index))
 	}
 
 	pub fn get_entities(&self) -> Vec<&EntityInstance> {
@@ -145,6 +190,11 @@ impl<'w, 's> MapQuery<'w, 's> {
 			.map(MapQuery::get_entities_of)
 			.unwrap_or_default()
 	}
+	pub fn get_instance_refs(&self) -> Vec<InstanceRef> {
+		self.get_active_level()
+			.map(MapQuery::get_instance_refs_of)
+			.unwrap_or_default()
+	}
 
 	pub fn get_camera_bounds(&self) -> Option<CameraBounds> {
 		self.get_active_level().map(MapQuery::get_camera_bounds_of)
diff --git a/src/pregen.rs b/src/pregen.rs
new file mode 100644
index 0000000000000000000000000000000000000000..84cb9e467f672835f394014ae086631198e04356
--- /dev/null
+++ b/src/pregen.rs
@@ -0,0 +1,123 @@
+use bevy::prelude::{Handle, Image, TextureAtlas};
+use bevy::render::render_resource::TextureFormat;
+use bevy::render::texture::TextureFormatPixelInfo;
+
+use crate::{get_ldtk_tile_scale, Indexer, LdtkLayer, LdtkLevel};
+
+pub fn write_layer_to_texture(
+	layer: &LdtkLayer,
+	buffer: &mut Vec<u8>,
+	format: &TextureFormat,
+	image: &Image,
+	atlas: &TextureAtlas,
+) {
+	if !layer.has_tiles() {
+		return;
+	}
+
+	let tile_size = get_ldtk_tile_scale();
+	let layer_indexer = Indexer::new(
+		layer.indexer().width() * tile_size as i64,
+		layer.indexer().height() * tile_size as i64,
+	);
+	let texture_indexer = Indexer::new(image.size().x, image.size().y);
+
+	layer.for_each_tile(|x, y, tile| {
+		let part = match atlas.textures.get(tile.tile.t as usize) {
+			Some(rect) => rect,
+			None => return,
+		};
+
+		let real_x = x * tile_size as i64;
+		let real_y = y * tile_size as i64;
+
+		for relative_x in 0..part.width() as usize {
+			for relative_y in 0..part.height() as usize {
+				let tile_image_x = part.min.x as usize + relative_x;
+				let tile_image_y = part.min.y as usize + relative_y;
+				let tile_index = texture_indexer.index(tile_image_x, tile_image_y);
+
+				let output_x = real_x as usize + relative_x;
+				let output_y = real_y as usize + relative_y;
+				let output_index = layer_indexer.index(output_x, output_y);
+
+				if let Some(target_pixel) =
+					image.data.chunks_exact(format.pixel_size()).nth(tile_index)
+				{
+					if let Some(map_px) = buffer
+						.chunks_exact_mut(format.pixel_size())
+						.nth(output_index)
+					{
+						if target_pixel.iter().any(|p| p != &0) {
+							map_px.copy_from_slice(target_pixel);
+						}
+					}
+				};
+			}
+		}
+	});
+}
+
+pub fn write_map_to_texture(
+	level: &LdtkLevel,
+	buffer: &mut Vec<u8>,
+	format: &TextureFormat,
+	image: &Image,
+	atlas: &TextureAtlas,
+) {
+	for layer in level.layers() {
+		write_layer_to_texture(layer, buffer, format, image, atlas);
+	}
+}
+
+pub trait SuppliesTextureAtlas {
+	fn get_atlas_handle(&self, name: impl ToString) -> Option<&Handle<TextureAtlas>>;
+	fn get_atlas(&self, name: &Handle<TextureAtlas>) -> Option<&TextureAtlas>;
+}
+pub trait SuppliesImage {
+	fn get_image_handle(&self, name: impl ToString) -> Option<&Handle<Image>>;
+	fn get_image(&self, handle: &Handle<Image>) -> Option<&Image>;
+}
+
+pub trait Rasterise {
+	fn write_to_texture(
+		&self,
+		buffer: &mut Vec<u8>,
+		format: &TextureFormat,
+		images: &impl SuppliesImage,
+		atlas: &impl SuppliesTextureAtlas,
+	);
+}
+
+impl Rasterise for LdtkLevel {
+	fn write_to_texture(
+		&self,
+		buffer: &mut Vec<u8>,
+		format: &TextureFormat,
+		images: &impl SuppliesImage,
+		atlas: &impl SuppliesTextureAtlas,
+	) {
+		for layer in self.layers() {
+			layer.write_to_texture(buffer, format, images, atlas);
+		}
+	}
+}
+impl Rasterise for LdtkLayer {
+	fn write_to_texture(
+		&self,
+		buffer: &mut Vec<u8>,
+		format: &TextureFormat,
+		images: &impl SuppliesImage,
+		atlas: &impl SuppliesTextureAtlas,
+	) {
+		if let Some(atlas) = self
+			.infer_tileset_name()
+			.and_then(|tn| atlas.get_atlas_handle(tn))
+			.and_then(|hn| atlas.get_atlas(hn))
+		{
+			if let Some(image) = images.get_image(&atlas.texture) {
+				write_layer_to_texture(self, buffer, format, image, atlas);
+			}
+		}
+	}
+}
diff --git a/src/types.rs b/src/types.rs
index 7fa9a4b742a0e22d404cb59f5f064fb5ea63e0f4..676ad47051b83562a6fba47abd3f0d1f8e1797d2 100644
--- a/src/types.rs
+++ b/src/types.rs
@@ -5,9 +5,16 @@ use bevy::math::{IVec2, UVec2};
 use bevy::utils::HashMap;
 use ldtk_rust::{LayerInstance, Level, TileInstance};
 use num_traits::AsPrimitive;
+use quadtree_rs::area::{Area, AreaBuilder};
+use quadtree_rs::point::Point;
+use quadtree_rs::Quadtree;
+use serde_json::Value;
 
 use crate::utils::{Indexer, SerdeClone};
-use crate::{get_ldtk_tile_scale, px_to_grid};
+use crate::{get_ldtk_tile_scale, px_to_grid, MapQuery};
+
+#[derive(Default, Clone, Debug, Ord, PartialOrd, PartialEq, Eq)]
+pub struct LevelDataUpdated(pub String);
 
 pub struct TileRef<'a> {
 	pub tile: &'a TileInstance,
@@ -89,49 +96,146 @@ impl From<IVec2> for SpatialIndex {
 	}
 }
 
+pub type ColliderQuadtree = Quadtree<i64, LocatedEntity>;
+
 pub struct LdtkLevel {
 	level: Level,
+	properties: HashMap<String, Value>,
 	processed_layers: Vec<LdtkLayer>,
+	colliders: ColliderQuadtree,
 }
 
 impl LdtkLevel {
+	pub fn width(&self) -> f32 {
+		self.level.px_wid as f32
+	}
+	pub fn width_i(&self) -> i64 {
+		self.level.px_wid
+	}
+	pub fn height(&self) -> f32 {
+		self.level.px_hei as f32
+	}
+	pub fn height_i(&self) -> i64 {
+		self.level.px_hei
+	}
 	pub fn level_ref(&self) -> &Level {
 		&self.level
 	}
 	pub fn level_ref_mut(&mut self) -> &mut Level {
 		&mut self.level
 	}
+	pub fn colliders_ref(&self) -> &ColliderQuadtree {
+		&self.colliders
+	}
+	pub fn colliders_ref_mut(&mut self) -> &mut ColliderQuadtree {
+		&mut self.colliders
+	}
 	pub fn layers(&self) -> impl DoubleEndedIterator<Item = &LdtkLayer> {
 		self.processed_layers.iter()
 	}
 	pub fn layers_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut LdtkLayer> {
 		self.processed_layers.iter_mut()
 	}
+	pub fn properties_ref(&self) -> &HashMap<String, Value> {
+		&self.properties
+	}
+	pub fn properties_mut(&mut self) -> &mut HashMap<String, Value> {
+		&mut self.properties
+	}
+	pub fn property(&self, name: impl ToString) -> Option<&Value> {
+		self.properties.get(&name.to_string())
+	}
+	pub fn property_or_null(&self, name: impl ToString) -> &Value {
+		self.property(name).unwrap_or_else(|| &Value::Null)
+	}
 	pub fn get_indexer(&self) -> Indexer {
 		Indexer::new(px_to_grid(self.level.px_wid), px_to_grid(self.level.px_hei))
 	}
 }
 
+#[derive(Debug, Clone, Default)]
+pub struct LocatedEntity {
+	pub position: IVec2,
+	pub size: IVec2,
+	pub properties: HashMap<String, Value>,
+	pub collides: bool,
+}
+
 impl From<Level> for LdtkLevel {
 	fn from(mut value: Level) -> Self {
 		let layers = value.layer_instances.take();
+		let mut properties = HashMap::with_capacity(value.field_instances.len());
+		let fields = std::mem::take(&mut value.field_instances);
 
-		Self {
+		for field in fields {
+			properties.insert(field.identifier, field.value.unwrap_or_else(|| Value::Null));
+		}
+
+		let level_width = value.px_wid;
+		let level_height = value.px_hei;
+
+		let mut level = Self {
+			colliders: Quadtree::new(0),
 			processed_layers: layers
 				.unwrap_or_default()
 				.into_iter()
+				.rev()
 				.enumerate()
 				.map(|(idx, inst)| LdtkLayer::new(idx, inst))
 				.collect(),
 			level: value,
+			properties,
+		};
+
+		let mut collider_quads = Quadtree::<i64, LocatedEntity>::new(32);
+		for entity in MapQuery::get_entities_of(&level) {
+			if entity.tags.contains(&String::from("Collider")) {
+				let mut properties = HashMap::with_capacity(entity.field_instances.len());
+				for field in entity.field_instances.iter() {
+					properties.insert(
+						field.identifier.clone(),
+						field.value.as_ref().unwrap_or_else(|| &Value::Null).clone(),
+					);
+				}
+
+				let width = entity.width;
+				let height = entity.height;
+				let left_x = entity.px[0];
+				let bottom_y = level_height - (entity.px[1] + height);
+
+				let size = IVec2::new(entity.width as i32, entity.height as i32);
+				let position = IVec2::new(level.level.px_wid as i32, level.level.px_hei as i32)
+					- (IVec2::new(entity.px[0] as i32, entity.px[1] as i32) + size);
+
+				let entity = LocatedEntity {
+					position,
+					size,
+					properties,
+					collides: true,
+				};
+
+				match AreaBuilder::default()
+					.anchor(Point::from((position.x as i64, position.y as i64)))
+					.dimensions((size.x as i64, size.y as i64))
+					.build()
+				{
+					Ok(point) => {
+						collider_quads.insert(point, entity);
+					}
+					Err(_) => {}
+				}
+			}
 		}
+
+		level.colliders = collider_quads;
+		level
 	}
 }
 
 pub struct LdtkLayer {
+	pub level: usize,
 	layer: LayerInstance,
 	position_lookup: HashMap<SpatialIndex, TileInstance>,
-	level: usize,
 	indexer: Indexer,
 }
 
@@ -159,6 +263,10 @@ impl LdtkLayer {
 		}
 	}
 
+	pub fn name(&self) -> &String {
+		&self.layer.identifier
+	}
+
 	pub fn indexer(&self) -> Indexer {
 		self.indexer
 	}