diff --git a/Cargo.lock b/Cargo.lock
index 2b1eb01f7a3e6b0de30b7b0ffe52eb358e6307b8..d190fa91354e8677b847b62f33693b5bb5a7cce0 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1870,6 +1870,7 @@ dependencies = [
  "bevy",
  "derivative",
  "flo_binding",
+ "kayak_font",
  "kayak_render_macros",
  "morphorm",
  "resources",
diff --git a/Cargo.toml b/Cargo.toml
index 01e8e8ea5ddf90b1f9ee108803eeedf008e8d951..1aadade977b5a319e34f5905e1d8294319e24315 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -11,16 +11,16 @@ members = ["bevy_kayak_ui", "kayak_core", "kayak_render_macros", "kayak_font"]
 default = ["bevy_renderer"]
 bevy_renderer = [
     "bevy_kayak_ui",
-    "kayak_font",
     "kayak_core/bevy_renderer",
+    "kayak_font/bevy_renderer",
     "bevy",
 ]
 
 [dependencies]
 bevy = { version = "0.6.0", optional = true }
-kayak_core = { path = "kayak_core" }
 bevy_kayak_ui = { path = "bevy_kayak_ui", optional = true }
-kayak_font = { path = "kayak_font", optional = true }
+kayak_core = { path = "kayak_core" }
+kayak_font = { path = "kayak_font" }
 kayak_render_macros = { path = "kayak_render_macros" }
 
 [dev-dependencies]
diff --git a/bevy_kayak_ui/src/render/mod.rs b/bevy_kayak_ui/src/render/mod.rs
index 476057abc98ba36f255d7212d66015bfb258563d..0a3babef1e0b257b28274aed1c8ca039584d9397 100644
--- a/bevy_kayak_ui/src/render/mod.rs
+++ b/bevy_kayak_ui/src/render/mod.rs
@@ -23,17 +23,17 @@ mod ui_pass_driver;
 pub mod unified;
 
 pub mod node {
-    pub const UI_PASS_DEPENDENCIES: &str = "ui_pass_dependencies";
-    pub const UI_PASS_DRIVER: &str = "ui_pass_driver";
+    pub const UI_PASS_DEPENDENCIES: &str = "kayak_ui_pass_dependencies";
+    pub const UI_PASS_DRIVER: &str = "kayak_ui_pass_driver";
 }
 
 pub mod draw_ui_graph {
-    pub const NAME: &str = "draw_ui";
+    pub const NAME: &str = "kayak_draw_ui";
     pub mod input {
-        pub const VIEW_ENTITY: &str = "view_entity";
+        pub const VIEW_ENTITY: &str = "kayak_view_entity";
     }
     pub mod node {
-        pub const MAIN_PASS: &str = "ui_pass";
+        pub const MAIN_PASS: &str = "kayak_ui_pass";
     }
 }
 
diff --git a/bevy_kayak_ui/src/render/unified/font/extract.rs b/bevy_kayak_ui/src/render/unified/font/extract.rs
index 783d149b26f84432d5829a19fa9b9c7e67ecd003..02228407791349f4055ad6b65565b4f0c75332fc 100644
--- a/bevy_kayak_ui/src/render/unified/font/extract.rs
+++ b/bevy_kayak_ui/src/render/unified/font/extract.rs
@@ -45,20 +45,22 @@ pub fn extract_texts(
     let chars_layouts = font.get_layout(
         CoordinateSystem::PositiveYDown,
         Alignment::Start,
-        Vec2::new(layout.posx, layout.posy + line_height),
-        Vec2::new(layout.width, layout.height),
+        (layout.posx, layout.posy + line_height),
+        (layout.width, layout.height),
         content,
         line_height,
         font_size,
     );
 
     for char_layout in chars_layouts {
+        let position = Vec2::new(char_layout.position.0, char_layout.position.1);
+        let size = Vec2::new(char_layout.size.0, char_layout.size.1);
         extracted_texts.push(ExtractQuadBundle {
             extracted_quad: ExtractedQuad {
                 font_handle: Some(font_handle.clone()),
                 rect: Rect {
-                    min: char_layout.position,
-                    max: char_layout.position + char_layout.size,
+                    min: position,
+                    max: position + size,
                 },
                 color: to_bevy_color(background_color),
                 vertex_index: 0,
diff --git a/bevy_kayak_ui/src/render/unified/font/mod.rs b/bevy_kayak_ui/src/render/unified/font/mod.rs
index 657c25a178e1bf73725f949668bcec0d79ab4ea0..62c83741a9946ee6c017cf552c9e3b4df62078f8 100644
--- a/bevy_kayak_ui/src/render/unified/font/mod.rs
+++ b/bevy_kayak_ui/src/render/unified/font/mod.rs
@@ -7,7 +7,7 @@ use bevy::{
         RenderApp, RenderStage,
     },
 };
-use kayak_font::{FontTextureCache, KayakFontPlugin};
+use kayak_font::bevy::{FontTextureCache, KayakFontPlugin};
 
 mod extract;
 mod font_mapping;
diff --git a/bevy_kayak_ui/src/render/unified/pipeline.rs b/bevy_kayak_ui/src/render/unified/pipeline.rs
index 9898e8c78278e58aaf93a5cdfe961e6b510bf7fd..57155b46d281765e94a3195c9afd0b25552f74a1 100644
--- a/bevy_kayak_ui/src/render/unified/pipeline.rs
+++ b/bevy_kayak_ui/src/render/unified/pipeline.rs
@@ -32,7 +32,10 @@ use bevy::{
     utils::HashMap,
 };
 use bytemuck::{Pod, Zeroable};
-use kayak_font::{FontRenderingPipeline, FontTextureCache, KayakFont};
+use kayak_font::{
+    bevy::{FontRenderingPipeline, FontTextureCache},
+    KayakFont,
+};
 
 use super::{Dpi, UNIFIED_SHADER_HANDLE};
 use crate::{render::ui_pass::TransparentUI, WindowSize};
diff --git a/kayak_core/Cargo.toml b/kayak_core/Cargo.toml
index 4057eeeeebc3d51c27159679665554f01008e441..610e7c9a043b544be58667ca759b46ae5c019f31 100644
--- a/kayak_core/Cargo.toml
+++ b/kayak_core/Cargo.toml
@@ -7,13 +7,14 @@ edition = "2021"
 
 [features]
 default = []
-bevy_renderer = ["bevy"]
+bevy_renderer = ["bevy", "kayak_font/bevy_renderer"]
 
 [dependencies]
 as-any = "0.2"
 derivative = "2.2"
 bevy = { version = "0.6.0", optional = true }
 flo_binding = { git = "https://github.com/StarArawn/flo_binding.git", rev = "c78431a56df5ec082b7e1c271871e6c0ac75e81e" }
+kayak_font = { path = "../kayak_font" }
 kayak_render_macros = { path = "../kayak_render_macros" }
 morphorm = { git = "https://github.com/geom3trik/morphorm", rev = "1243152d4cebea46fd3e5098df26402c73acae91" }
 resources = "1.1"
diff --git a/kayak_font/Cargo.toml b/kayak_font/Cargo.toml
index f3c489619cf4c7f9be64e8e085319b71e8374134..7f27ef978a4015e94ce876e9f4ee766d8ec16a6d 100644
--- a/kayak_font/Cargo.toml
+++ b/kayak_font/Cargo.toml
@@ -5,10 +5,14 @@ edition = "2021"
 
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
+[features]
+default = ["bevy_renderer"]
+bevy_renderer = ["bevy"]
+
 [dependencies]
 anyhow = { version = "1.0" }
-bevy = { version = "0.6.0" }
+bevy = { version = "0.6.0", optional = true }
 bytemuck = "1.7.2"
-serde = "1.0"
+serde = { version = "1.0", features = ["derive"] }
 serde_json = "1.0"
 serde_path_to_error = "0.1"
diff --git a/kayak_font/examples/bevy.rs b/kayak_font/examples/bevy.rs
index af68adb3adac1f841da7aef788dc76efc53a7fe9..9e000354e81af6e7cf0ed5c9ef0f1326f8a9760b 100644
--- a/kayak_font/examples/bevy.rs
+++ b/kayak_font/examples/bevy.rs
@@ -5,7 +5,7 @@ use bevy::{
     window::WindowDescriptor,
     DefaultPlugins,
 };
-use kayak_font::{Alignment, KayakFont, KayakFontPlugin};
+use kayak_font::{Alignment, KayakFont, bevy::KayakFontPlugin};
 
 mod renderer;
 use renderer::FontRenderPlugin;
diff --git a/kayak_font/examples/renderer/extract.rs b/kayak_font/examples/renderer/extract.rs
index c5f8e342791f9d8ddd5aeae53416421420cd1b31..3e874c97cb7b16e6f8e739d097a79bb57a82dc25 100644
--- a/kayak_font/examples/renderer/extract.rs
+++ b/kayak_font/examples/renderer/extract.rs
@@ -1,6 +1,6 @@
 use bevy::{
     prelude::{Assets, Commands, Handle, Query, Res},
-    sprite::Rect,
+    sprite::Rect, math::Vec2,
 };
 use kayak_font::{CoordinateSystem, KayakFont};
 
@@ -21,20 +21,23 @@ pub fn extract(
             let layouts = font.get_layout(
                 CoordinateSystem::PositiveYUp,
                 text.horz_alignment,
-                text.position,
-                text.size,
+                (text.position.x, text.position.y),
+                (text.size.x, text.size.y),
                 &text.content,
                 text.line_height,
                 text.font_size,
             );
 
             for layout in layouts {
+                let position = Vec2::new(layout.position.0, layout.position.1);
+                let size = Vec2::new(layout.size.0, layout.size.1);
+
                 extracted_texts.push(ExtractCharBundle {
                     extracted_quad: ExtractedChar {
                         font_handle: Some(font_handle.clone()),
                         rect: Rect {
-                            min: layout.position,
-                            max: layout.position + layout.size,
+                            min: position,
+                            max: position + size,
                         },
                         color: text.color,
                         vertex_index: 0,
diff --git a/kayak_font/examples/renderer/mod.rs b/kayak_font/examples/renderer/mod.rs
index ae223b2ee3b11c514c88d12d7f6d8b61cb0c2e06..dec8300fd080f6cd14ab66e885f83878177a8ab5 100644
--- a/kayak_font/examples/renderer/mod.rs
+++ b/kayak_font/examples/renderer/mod.rs
@@ -11,7 +11,7 @@ use bevy::{
         RenderApp, RenderStage,
     },
 };
-use kayak_font::FontTextureCache;
+use kayak_font::bevy::FontTextureCache;
 
 use self::pipeline::{DrawUI, FontPipeline, QuadMeta};
 
diff --git a/kayak_font/examples/renderer/pipeline.rs b/kayak_font/examples/renderer/pipeline.rs
index 2b023fe306b8bd838425fc74370ba9841741be9f..7062e6e1d86910318b602836f9e3d4de2998f2bd 100644
--- a/kayak_font/examples/renderer/pipeline.rs
+++ b/kayak_font/examples/renderer/pipeline.rs
@@ -28,7 +28,10 @@ use bevy::{
     sprite::Rect,
 };
 use bytemuck::{Pod, Zeroable};
-use kayak_font::{FontRenderingPipeline, FontTextureCache, KayakFont};
+use kayak_font::{
+    bevy::{FontRenderingPipeline, FontTextureCache},
+    KayakFont,
+};
 
 use super::FONT_SHADER_HANDLE;
 
@@ -169,7 +172,7 @@ impl FromWorld for FontPipeline {
             },
             depth_stencil: None,
             multisample: MultisampleState {
-                count: 1,
+                count: 4,
                 mask: !0,
                 alpha_to_coverage_enabled: false,
             },
diff --git a/kayak_font/src/font.rs b/kayak_font/src/font.rs
index 1481c1a121cd146ed360ab6b47ab833c8f95826d..72307f34aad6def3f477b542bdb3ecd90aa3272a 100644
--- a/kayak_font/src/font.rs
+++ b/kayak_font/src/font.rs
@@ -1,15 +1,11 @@
 use std::collections::HashMap;
 
-use bevy::{
-    asset::{AssetLoader, AssetPath, BoxedFuture, LoadContext, LoadedAsset},
-    math::Vec2,
-    prelude::Handle,
-    reflect::TypeUuid,
-    render::texture::Image,
-};
+#[cfg(feature = "bevy_renderer")]
+use bevy::{prelude::Handle, reflect::TypeUuid, render::texture::Image};
 
 use crate::Sdf;
 
+#[cfg(feature = "bevy_renderer")]
 #[derive(Debug, Clone, TypeUuid)]
 #[uuid = "4fe4732c-6731-49bb-bafc-4690d636b848"]
 pub struct KayakFont {
@@ -18,10 +14,17 @@ pub struct KayakFont {
     char_ids: HashMap<char, u32>,
 }
 
+#[cfg(not(feature = "bevy_renderer"))]
+#[derive(Debug, Clone)]
+pub struct KayakFont {
+    pub sdf: Sdf,
+    char_ids: HashMap<char, u32>,
+}
+
 #[derive(Default, Debug, Clone, Copy)]
 pub struct LayoutRect {
-    pub position: Vec2,
-    pub size: Vec2,
+    pub position: (f32, f32),
+    pub size: (f32, f32),
     pub content: char,
 }
 
@@ -39,9 +42,10 @@ pub enum Alignment {
 }
 
 impl KayakFont {
-    pub fn new(sdf: Sdf, atlas_image: Handle<Image>) -> Self {
+    pub fn new(sdf: Sdf, #[cfg(feature = "bevy_renderer")] atlas_image: Handle<Image>) -> Self {
         Self {
             sdf,
+            #[cfg(feature = "bevy_renderer")]
             atlas_image,
             char_ids: HashMap::default(),
         }
@@ -68,8 +72,8 @@ impl KayakFont {
                     Some(val) => (
                         val.left,
                         val.top,
-                        val.size().x * font_size,
-                        val.size().y * font_size,
+                        val.size().0 * font_size,
+                        val.size().1 * font_size,
                     ),
                     None => (0.0, 0.0, 0.0, 0.0),
                 };
@@ -85,8 +89,8 @@ impl KayakFont {
         &self,
         axis_alignment: CoordinateSystem,
         alignment: Alignment,
-        position: Vec2,
-        max_size: Vec2,
+        position: (f32, f32),
+        max_size: (f32, f32),
         content: &String,
         line_height: f32,
         font_size: f32,
@@ -94,7 +98,7 @@ impl KayakFont {
         let mut positions_and_size = Vec::new();
         let max_glyph_size = self.sdf.max_glyph_size();
         let font_ratio = font_size / self.sdf.atlas.size;
-        let resized_max_glyph_size = (max_glyph_size.x * font_ratio, max_glyph_size.y * font_ratio);
+        let resized_max_glyph_size = (max_glyph_size.0 * font_ratio, max_glyph_size.1 * font_ratio);
 
         // TODO: Make this configurable?
         let split_chars = vec![' ', '\t', '-', '\n'];
@@ -117,7 +121,7 @@ impl KayakFont {
         let mut last_width = 0.0;
         for word in content.split(&split_chars[..]) {
             let word_width = self.get_word_width(word, font_size);
-            if x + word_width > max_size.x {
+            if x + word_width > max_size.0 {
                 y -= shift_sign * line_height;
                 line_widths.push((x, line_starting_index, positions_and_size.len()));
                 line_starting_index = positions_and_size.len();
@@ -130,8 +134,8 @@ impl KayakFont {
                         Some(val) => (
                             val.left,
                             val.top,
-                            val.size().x * font_size,
-                            val.size().y * font_size,
+                            val.size().0 * font_size,
+                            val.size().1 * font_size,
                         ),
                         None => (0.0, 0.0, 0.0, 0.0),
                     };
@@ -142,8 +146,8 @@ impl KayakFont {
                     let position_y = y + (shift_sign * top * font_size);
 
                     positions_and_size.push(LayoutRect {
-                        position: Vec2::new(position_x, position_y),
-                        size: Vec2::new(resized_max_glyph_size.0, resized_max_glyph_size.1),
+                        position: (position_x, position_y),
+                        size: (resized_max_glyph_size.0, resized_max_glyph_size.1),
                         content: c,
                     });
 
@@ -172,50 +176,17 @@ impl KayakFont {
         for (line_width, starting_index, end_index) in line_widths {
             let shift_x = match alignment {
                 Alignment::Start => 0.0,
-                Alignment::Middle => (max_size.x - line_width) / 2.0,
-                Alignment::End => max_size.x - line_width,
+                Alignment::Middle => (max_size.0 - line_width) / 2.0,
+                Alignment::End => max_size.0 - line_width,
             };
             for i in starting_index..end_index {
                 let layout_rect = &mut positions_and_size[i];
 
-                layout_rect.position.x += position.x + shift_x;
-                layout_rect.position.y += position.y;
+                layout_rect.position.0 += position.0 + shift_x;
+                layout_rect.position.1 += position.1;
             }
         }
 
         positions_and_size
     }
 }
-
-#[derive(Default)]
-pub struct KayakFontLoader;
-
-impl AssetLoader for KayakFontLoader {
-    fn load<'a>(
-        &'a self,
-        bytes: &'a [u8],
-        load_context: &'a mut LoadContext,
-    ) -> BoxedFuture<'a, Result<(), anyhow::Error>> {
-        Box::pin(async move {
-            let path = load_context.path();
-            let path = path.with_extension("png");
-            let atlas_image_path = AssetPath::new(path, None);
-            let mut font = KayakFont::new(
-                Sdf::from_bytes(bytes),
-                load_context.get_handle(atlas_image_path.clone()),
-            );
-
-            font.generate_char_ids();
-
-            load_context
-                .set_default_asset(LoadedAsset::new(font).with_dependency(atlas_image_path));
-
-            Ok(())
-        })
-    }
-
-    fn extensions(&self) -> &[&str] {
-        static EXTENSIONS: &[&str] = &["kayak_font"];
-        EXTENSIONS
-    }
-}
diff --git a/kayak_font/src/glyph.rs b/kayak_font/src/glyph.rs
index f883522fc5e9932423ad33cb4ab672ac45ed70ae..c0b16386e91f46159901d3d1e3849b199b512ea1 100644
--- a/kayak_font/src/glyph.rs
+++ b/kayak_font/src/glyph.rs
@@ -1,4 +1,3 @@
-use bevy::math::Vec2;
 use serde::{Deserialize, Deserializer};
 
 fn from_u32<'de, D>(deserializer: D) -> Result<char, D::Error>
@@ -40,7 +39,7 @@ impl Rect {
         self.top - self.bottom
     }
 
-    pub fn size(&self) -> Vec2 {
-        Vec2::new(self.width(), self.height())
+    pub fn size(&self) -> (f32, f32) {
+        (self.width(), self.height())
     }
 }
diff --git a/kayak_font/src/lib.rs b/kayak_font/src/lib.rs
index 2582c37f13b1f5b6a7b91c9be57132b9831af143..71f18cf7ceae55dd5471934d37ef2260012d33a6 100644
--- a/kayak_font/src/lib.rs
+++ b/kayak_font/src/lib.rs
@@ -2,144 +2,183 @@ mod atlas;
 mod font;
 mod glyph;
 mod metrics;
-mod renderer;
 mod sdf;
 
 pub use atlas::*;
-use bevy::{
-    prelude::{
-        AddAsset, AssetEvent, Assets, Commands, EventReader, Handle, Local, Plugin, Res, ResMut,
-    },
-    render::{
-        render_resource::{FilterMode, TextureFormat, TextureUsages},
-        texture::Image,
-        RenderApp, RenderStage,
-    },
-    utils::HashSet,
-};
 pub use font::*;
 pub use glyph::*;
 pub use metrics::*;
 pub use sdf::*;
 
-pub use renderer::*;
+#[cfg(feature = "bevy_renderer")]
+mod renderer;
 
-pub struct KayakFontPlugin;
+#[cfg(feature = "bevy_renderer")]
+pub mod bevy {
+    pub use crate::renderer::*;
+    use crate::{KayakFont, Sdf};
+    use bevy::{
+        asset::{AssetLoader, AssetPath, BoxedFuture, LoadContext, LoadedAsset},
+        prelude::{
+            AddAsset, AssetEvent, Assets, Commands, EventReader, Handle, Local, Plugin, Res, ResMut,
+        },
+        render::{
+            render_resource::{FilterMode, TextureFormat, TextureUsages},
+            texture::Image,
+            RenderApp, RenderStage,
+        },
+        utils::HashSet,
+    };
+    pub struct KayakFontPlugin;
 
-impl Plugin for KayakFontPlugin {
-    fn build(&self, app: &mut bevy::prelude::App) {
-        app.add_asset::<KayakFont>()
-            .add_asset_loader(KayakFontLoader)
-            .add_system(init_font_texture);
+    impl Plugin for KayakFontPlugin {
+        fn build(&self, app: &mut bevy::prelude::App) {
+            app.add_asset::<KayakFont>()
+                .add_asset_loader(KayakFontLoader)
+                .add_system(init_font_texture);
 
-        let render_app = app.sub_app_mut(RenderApp);
-        render_app
-            .init_resource::<FontTextureCache>()
-            .init_resource::<ExtractedFonts>()
-            .add_system_to_stage(RenderStage::Extract, extract_fonts)
-            .add_system_to_stage(RenderStage::Prepare, prepare_fonts);
+            let render_app = app.sub_app_mut(RenderApp);
+            render_app
+                .init_resource::<FontTextureCache>()
+                .init_resource::<ExtractedFonts>()
+                .add_system_to_stage(RenderStage::Extract, extract_fonts)
+                .add_system_to_stage(RenderStage::Prepare, prepare_fonts);
+        }
     }
-}
 
-pub fn init_font_texture(
-    mut not_processed: Local<Vec<Handle<KayakFont>>>,
-    mut font_events: EventReader<AssetEvent<KayakFont>>,
-    mut images: ResMut<Assets<Image>>,
-    fonts: Res<Assets<KayakFont>>,
-) {
-    // quick and dirty, run this for all textures anytime a texture is created.
-    for event in font_events.iter() {
-        match event {
-            AssetEvent::Created { handle } => {
-                not_processed.push(handle.clone_weak());
+    pub fn init_font_texture(
+        mut not_processed: Local<Vec<Handle<KayakFont>>>,
+        mut font_events: EventReader<AssetEvent<KayakFont>>,
+        mut images: ResMut<Assets<Image>>,
+        fonts: Res<Assets<KayakFont>>,
+    ) {
+        // quick and dirty, run this for all textures anytime a texture is created.
+        for event in font_events.iter() {
+            match event {
+                AssetEvent::Created { handle } => {
+                    not_processed.push(handle.clone_weak());
+                }
+                _ => (),
             }
-            _ => (),
         }
-    }
 
-    let not_processed_fonts = not_processed.drain(..).collect::<Vec<_>>();
-    for font_handle in not_processed_fonts {
-        if let Some(font) = fonts.get(&font_handle) {
-            if let Some(mut texture) = images.get_mut(&font.atlas_image) {
-                texture.texture_descriptor.format = TextureFormat::Rgba8Unorm;
-                texture.sampler_descriptor.min_filter = FilterMode::Linear;
-                texture.sampler_descriptor.mipmap_filter = FilterMode::Linear;
-                texture.sampler_descriptor.mag_filter = FilterMode::Linear;
-                texture.texture_descriptor.usage = TextureUsages::TEXTURE_BINDING
-                    | TextureUsages::COPY_DST
-                    | TextureUsages::COPY_SRC;
-            } else {
-                not_processed.push(font_handle.clone_weak());
+        let not_processed_fonts = not_processed.drain(..).collect::<Vec<_>>();
+        for font_handle in not_processed_fonts {
+            if let Some(font) = fonts.get(&font_handle) {
+                if let Some(mut texture) = images.get_mut(&font.atlas_image) {
+                    texture.texture_descriptor.format = TextureFormat::Rgba8Unorm;
+                    texture.sampler_descriptor.min_filter = FilterMode::Linear;
+                    texture.sampler_descriptor.mipmap_filter = FilterMode::Linear;
+                    texture.sampler_descriptor.mag_filter = FilterMode::Linear;
+                    texture.texture_descriptor.usage = TextureUsages::TEXTURE_BINDING
+                        | TextureUsages::COPY_DST
+                        | TextureUsages::COPY_SRC;
+                } else {
+                    not_processed.push(font_handle.clone_weak());
+                }
             }
         }
     }
-}
 
-#[derive(Default)]
-pub struct ExtractedFonts {
-    pub fonts: Vec<(Handle<KayakFont>, KayakFont)>,
-}
+    #[derive(Default)]
+    pub struct ExtractedFonts {
+        pub fonts: Vec<(Handle<KayakFont>, KayakFont)>,
+    }
 
-fn extract_fonts(
-    mut not_processed: Local<Vec<Handle<KayakFont>>>,
-    mut commands: Commands,
-    font_assets: Res<Assets<KayakFont>>,
-    mut events: EventReader<AssetEvent<KayakFont>>,
-    textures: Res<Assets<Image>>,
-) {
-    let mut extracted_fonts = ExtractedFonts { fonts: Vec::new() };
-    let mut changed_assets = HashSet::default();
-    let mut removed = Vec::new();
-    for event in events.iter() {
-        match event {
-            AssetEvent::Created { handle } => {
-                changed_assets.insert(handle.clone_weak());
-            }
-            AssetEvent::Modified { handle } => {
-                changed_assets.insert(handle.clone_weak());
-            }
-            AssetEvent::Removed { handle } => {
-                if !changed_assets.remove(handle) {
-                    removed.push(handle.clone_weak());
+    fn extract_fonts(
+        mut not_processed: Local<Vec<Handle<KayakFont>>>,
+        mut commands: Commands,
+        font_assets: Res<Assets<KayakFont>>,
+        mut events: EventReader<AssetEvent<KayakFont>>,
+        textures: Res<Assets<Image>>,
+    ) {
+        let mut extracted_fonts = ExtractedFonts { fonts: Vec::new() };
+        let mut changed_assets = HashSet::default();
+        let mut removed = Vec::new();
+        for event in events.iter() {
+            match event {
+                AssetEvent::Created { handle } => {
+                    changed_assets.insert(handle.clone_weak());
+                }
+                AssetEvent::Modified { handle } => {
+                    changed_assets.insert(handle.clone_weak());
+                }
+                AssetEvent::Removed { handle } => {
+                    if !changed_assets.remove(handle) {
+                        removed.push(handle.clone_weak());
+                    }
                 }
             }
         }
-    }
 
-    for handle in not_processed.drain(..) {
-        changed_assets.insert(handle);
-    }
+        for handle in not_processed.drain(..) {
+            changed_assets.insert(handle);
+        }
 
-    for handle in changed_assets {
-        let font_asset = font_assets.get(&handle).unwrap();
-        if let Some(image) = textures.get(&font_asset.atlas_image) {
-            if !image
-                .texture_descriptor
-                .usage
-                .contains(TextureUsages::COPY_SRC)
-                || image.texture_descriptor.format != TextureFormat::Rgba8Unorm
-            {
+        for handle in changed_assets {
+            let font_asset = font_assets.get(&handle).unwrap();
+            if let Some(image) = textures.get(&font_asset.atlas_image) {
+                if !image
+                    .texture_descriptor
+                    .usage
+                    .contains(TextureUsages::COPY_SRC)
+                    || image.texture_descriptor.format != TextureFormat::Rgba8Unorm
+                {
+                    not_processed.push(handle);
+                    continue;
+                }
+            } else {
                 not_processed.push(handle);
                 continue;
             }
-        } else {
-            not_processed.push(handle);
-            continue;
+
+            let font = font_asset.clone();
+            extracted_fonts.fonts.push((handle, font));
         }
 
-        let font = font_asset.clone();
-        extracted_fonts.fonts.push((handle, font));
+        commands.insert_resource(extracted_fonts);
     }
 
-    commands.insert_resource(extracted_fonts);
-}
+    fn prepare_fonts(
+        mut extracted_fonts: ResMut<ExtractedFonts>,
+        mut font_texture_cache: ResMut<FontTextureCache>,
+    ) {
+        let fonts: Vec<_> = extracted_fonts.fonts.drain(..).collect();
+        for (handle, font) in fonts {
+            font_texture_cache.add(handle, font);
+        }
+    }
 
-fn prepare_fonts(
-    mut extracted_fonts: ResMut<ExtractedFonts>,
-    mut font_texture_cache: ResMut<FontTextureCache>,
-) {
-    let fonts: Vec<_> = extracted_fonts.fonts.drain(..).collect();
-    for (handle, font) in fonts {
-        font_texture_cache.add(handle, font);
+    #[derive(Default)]
+    pub struct KayakFontLoader;
+
+    impl AssetLoader for KayakFontLoader {
+        fn load<'a>(
+            &'a self,
+            bytes: &'a [u8],
+            load_context: &'a mut LoadContext,
+        ) -> BoxedFuture<'a, Result<(), anyhow::Error>> {
+            Box::pin(async move {
+                let path = load_context.path();
+                let path = path.with_extension("png");
+                let atlas_image_path = AssetPath::new(path, None);
+                let mut font = KayakFont::new(
+                    Sdf::from_bytes(bytes),
+                    load_context.get_handle(atlas_image_path.clone()),
+                );
+
+                font.generate_char_ids();
+
+                load_context
+                    .set_default_asset(LoadedAsset::new(font).with_dependency(atlas_image_path));
+
+                Ok(())
+            })
+        }
+
+        fn extensions(&self) -> &[&str] {
+            static EXTENSIONS: &[&str] = &["kayak_font"];
+            EXTENSIONS
+        }
     }
 }
diff --git a/kayak_font/src/renderer/font_texture_cache.rs b/kayak_font/src/renderer/font_texture_cache.rs
index 1a2ad1e16744da55c25fd036ae57d87276e69a60..38a4c6211e2bc9395629ebf19f8148b34f382467 100644
--- a/kayak_font/src/renderer/font_texture_cache.rs
+++ b/kayak_font/src/renderer/font_texture_cache.rs
@@ -84,7 +84,7 @@ impl FontTextureCache {
                         queue,
                         pipeline,
                         atlas_texture,
-                        font.sdf.max_glyph_size(),
+                        Vec2::new(font.sdf.max_glyph_size().0, font.sdf.max_glyph_size().1),
                     );
                 } else {
                     was_processed = false;
@@ -291,8 +291,8 @@ impl FontTextureCache {
                         aspect: TextureAspect::All,
                     },
                     Extent3d {
-                        width: glyph_size.x as u32,
-                        height: glyph_size.y as u32,
+                        width: glyph_size.0 as u32,
+                        height: glyph_size.1 as u32,
                         depth_or_array_layers: 1,
                     },
                 );
diff --git a/kayak_font/src/sdf.rs b/kayak_font/src/sdf.rs
index f8000bbbe8fdbff0c81b78de1dc06b133df135d9..49e545361eae99786c20bb2e102c33eb7f54425a 100644
--- a/kayak_font/src/sdf.rs
+++ b/kayak_font/src/sdf.rs
@@ -1,5 +1,4 @@
 use crate::{atlas::Atlas, glyph::Glyph, metrics::Metrics};
-use bevy::math::Vec2;
 use serde::Deserialize;
 
 #[derive(Deserialize, Debug, Clone)]
@@ -10,7 +9,7 @@ pub struct Sdf {
     kerning: Vec<KerningData>,
 }
 
-#[derive(serde::Deserialize, Debug, Clone, Copy)]
+#[derive(Deserialize, Debug, Clone, Copy)]
 pub struct KerningData {
     pub unicode1: u32,
     pub unicode2: u32,
@@ -48,16 +47,16 @@ impl Sdf {
         value
     }
 
-    pub fn max_glyph_size(&self) -> Vec2 {
-        let mut size = Vec2::new(0.0, 0.0);
+    pub fn max_glyph_size(&self) -> (f32, f32) {
+        let mut size = (0.0, 0.0);
         self.glyphs.iter().for_each(|glyph| {
             if let Some(atlas_bounds) = glyph.atlas_bounds {
                 let atlas_size = atlas_bounds.size();
-                if atlas_size.x > size.x {
-                    size.x = atlas_size.x;
+                if atlas_size.0 > size.0 {
+                    size.0 = atlas_size.0;
                 }
-                if atlas_size.y > size.y {
-                    size.y = atlas_size.y;
+                if atlas_size.1 > size.1 {
+                    size.1 = atlas_size.1;
                 }
             }
         });
@@ -70,7 +69,7 @@ impl Sdf {
 fn test_sdf_loader() {
     use crate::SDFType;
     let sdf = Sdf::from_string(include_str!("../assets/roboto.kayak_font").to_string());
-    assert!(sdf.max_glyph_size() == Vec2::new(30.0, 36.0));
+    assert!(sdf.max_glyph_size() == (30.0, 36.0));
     assert!(sdf.atlas.width == 212);
     assert!(sdf.atlas.height == 212);
     assert!(matches!(sdf.atlas.sdf_type, SDFType::Msdf));