diff --git a/examples/scrolling.rs b/examples/scrolling.rs
index c4c23871bbcb5d7153edafebf6531b9250585c11..078f3ce7737677dc674241e7cb787984a24edde8 100644
--- a/examples/scrolling.rs
+++ b/examples/scrolling.rs
@@ -6,7 +6,13 @@ fn startup(
     mut font_mapping: ResMut<FontMapping>,
     asset_server: Res<AssetServer>,
 ) {
-    font_mapping.set_default(asset_server.load("roboto.kayak_font"));
+    let font_asset = asset_server.load("roboto.kayak_font");
+    font_mapping.set_default(font_asset.clone());
+
+    // You can force the entire font to use subpixel rendering.
+    // Note: The subpixel settings on the text widget or render command
+    // will be ignored if this setting is used.
+    font_mapping.force_subpixel(&font_asset);
 
     // Camera 2D forces a clear pass in bevy.
     // We do this because our scene is not rendering anything else.
diff --git a/examples/text.rs b/examples/text.rs
index 142c89d9c8fe4a7126932630f76738f721e2c32a..325c756327ed5362c0f225e7ab12c4b1c2232622 100644
--- a/examples/text.rs
+++ b/examples/text.rs
@@ -21,6 +21,7 @@ fn my_widget_1_render(
                 content: format!("My number is: {}", my_widget.foo),
                 alignment: Alignment::Start,
                 word_wrap: false,
+                subpixel: false,
             }),
             ..Default::default()
         }
diff --git a/examples/vec.rs b/examples/vec.rs
index adb28c31ac3fc3ca5d17d9a1f2db5d776d898b72..98c61427e034c1502b1cd5627c560046efb03dc3 100644
--- a/examples/vec.rs
+++ b/examples/vec.rs
@@ -1,5 +1,5 @@
 use bevy::prelude::*;
-use kayak_ui::prelude::{widgets::*, KStyle, *};
+use kayak_ui::prelude::{widgets::*, *};
 
 #[derive(Component, Default, PartialEq, Eq, Clone)]
 pub struct MyWidgetProps {}
diff --git a/src/render/font/extract.rs b/src/render/font/extract.rs
index e6051ccf5b7c580325259c9dc4973a65faa5676e..bba0488b75708df768d0621e574348f25e1b95f5 100644
--- a/src/render/font/extract.rs
+++ b/src/render/font/extract.rs
@@ -20,15 +20,17 @@ pub fn extract_texts(
     _dpi: f32,
 ) -> Vec<ExtractQuadBundle> {
     let mut extracted_texts = Vec::new();
-    let (background_color, text_layout, layout, font, properties) = match render_primitive {
+    let (background_color, text_layout, layout, font, properties, subpixel) = match render_primitive
+    {
         RenderPrimitive::Text {
             color,
             text_layout,
             layout,
             font,
             properties,
+            subpixel,
             ..
-        } => (color, text_layout, layout, font, *properties),
+        } => (color, text_layout, layout, font, *properties, subpixel),
         _ => panic!(""),
     };
 
@@ -40,6 +42,8 @@ pub fn extract_texts(
         }
     };
 
+    let forced = font_mapping.get_subpixel_forced(&font_handle);
+
     let base_position = Vec2::new(layout.posx, layout.posy + properties.font_size);
 
     for glyph_rect in text_layout.glyphs() {
@@ -60,7 +64,11 @@ pub fn extract_texts(
                 vertex_index: 0,
                 char_id: font.get_char_id(glyph_rect.content).unwrap(),
                 z_index: layout.z_index,
-                quad_type: UIQuadType::Text,
+                quad_type: if *subpixel || forced {
+                    UIQuadType::TextSubpixel
+                } else {
+                    UIQuadType::Text
+                },
                 type_index: 0,
                 border_radius: Corner::default(),
                 image: None,
diff --git a/src/render/font/font_mapping.rs b/src/render/font/font_mapping.rs
index df015c620ed19324f512cd2c7078a6218a42fd5b..bfd013416f54a760dc45a76ede8a450c565c921c 100644
--- a/src/render/font/font_mapping.rs
+++ b/src/render/font/font_mapping.rs
@@ -1,92 +1,107 @@
-use bevy::{
-    prelude::{Handle, Resource},
-    utils::HashMap,
-};
-use kayak_font::KayakFont;
-
-// use crate::context::Context;
-
-/// A resource used to manage fonts for use in a `KayakContext`
-///
-/// # Example
-///
-/// ```
-/// use bevy::prelude::*;
-/// use bevy_kayak_ui::FontMapping;
-///
-/// fn setup_ui(
-///   # mut commands: Commands,
-///   asset_server: Res<AssetServer>,
-///   mut font_mapping: ResMut<FontMapping>
-/// ) {
-///   # commands.spawn_bundle(UICameraBundle::new());
-///   #
-///   font_mapping.set_default(asset_server.load("roboto.kayak_font"));
-///   // ...
-///   #
-///   # let context = BevyContext::new(|context| {
-///   #   render! {
-///   #     <App>
-///   #       <Text content={"Hello World!".to_string()} />
-///   #     </App>
-///   #   }
-///   # });
-///   #
-///   # commands.insert_resource(context);
-/// }
-/// ```
-#[derive(Resource, Default)]
-pub struct FontMapping {
-    font_ids: HashMap<Handle<KayakFont>, String>,
-    font_handles: HashMap<String, Handle<KayakFont>>,
-    new_fonts: Vec<String>,
-}
-
-impl FontMapping {
-    /// Add a `KayakFont` to be tracked
-    pub fn add(&mut self, key: impl Into<String>, handle: Handle<KayakFont>) {
-        let key = key.into();
-        if !self.font_ids.contains_key(&handle) {
-            self.font_ids.insert(handle.clone(), key.clone());
-            self.new_fonts.push(key.clone());
-            self.font_handles.insert(key, handle);
-        }
-    }
-
-    /// Set a default `KayakFont`
-    pub fn set_default(&mut self, handle: Handle<KayakFont>) {
-        self.add(crate::DEFAULT_FONT, handle);
-    }
-
-    pub(crate) fn mark_all_as_new(&mut self) {
-        self.new_fonts.extend(self.font_handles.keys().cloned());
-    }
-
-    /// Get the handle for the given font name
-    pub fn get_handle(&self, id: String) -> Option<Handle<KayakFont>> {
-        self.font_handles.get(&id).cloned()
-    }
-
-    /// Get the font name for the given handle
-    pub fn get(&self, font: &Handle<KayakFont>) -> Option<String> {
-        self.font_ids.get(font).cloned()
-    }
-
-    // pub(crate) fn add_loaded_to_kayak(
-    //     &mut self,
-    //     fonts: &Res<Assets<KayakFont>>,
-    //     context: &Context,
-    // ) {
-    //     if let Ok(mut kayak_context) = context.kayak_context.write() {
-    //         let new_fonts = self.new_fonts.drain(..).collect::<Vec<_>>();
-    //         for font_key in new_fonts {
-    //             let font_handle = self.font_handles.get(&font_key).unwrap();
-    //             if let Some(font) = fonts.get(font_handle) {
-    //                 kayak_context.set_asset(font_key, font.clone());
-    //             } else {
-    //                 self.new_fonts.push(font_key);
-    //             }
-    //         }
-    //     }
-    // }
-}
+use bevy::{
+    prelude::{Handle, Resource},
+    utils::{HashMap, HashSet},
+};
+use kayak_font::KayakFont;
+
+// use crate::context::Context;
+
+/// A resource used to manage fonts for use in a `KayakContext`
+///
+/// # Example
+///
+/// ```
+/// use bevy::prelude::*;
+/// use bevy_kayak_ui::FontMapping;
+///
+/// fn setup_ui(
+///   # mut commands: Commands,
+///   asset_server: Res<AssetServer>,
+///   mut font_mapping: ResMut<FontMapping>
+/// ) {
+///   # commands.spawn_bundle(UICameraBundle::new());
+///   #
+///   font_mapping.set_default(asset_server.load("roboto.kayak_font"));
+///   // ...
+///   #
+///   # let context = BevyContext::new(|context| {
+///   #   render! {
+///   #     <App>
+///   #       <Text content={"Hello World!".to_string()} />
+///   #     </App>
+///   #   }
+///   # });
+///   #
+///   # commands.insert_resource(context);
+/// }
+/// ```
+#[derive(Resource, Default)]
+pub struct FontMapping {
+    font_ids: HashMap<Handle<KayakFont>, String>,
+    font_handles: HashMap<String, Handle<KayakFont>>,
+    new_fonts: Vec<String>,
+    subpixel: HashSet<Handle<KayakFont>>,
+}
+
+impl FontMapping {
+    /// Add a `KayakFont` to be tracked
+    pub fn add(&mut self, key: impl Into<String>, handle: Handle<KayakFont>) {
+        let key = key.into();
+        if !self.font_ids.contains_key(&handle) {
+            self.font_ids.insert(handle.clone(), key.clone());
+            self.new_fonts.push(key.clone());
+            self.font_handles.insert(key, handle);
+        }
+    }
+
+    /// Set a default `KayakFont`
+    pub fn set_default(&mut self, handle: Handle<KayakFont>) {
+        self.add(crate::DEFAULT_FONT, handle);
+    }
+
+    pub(crate) fn mark_all_as_new(&mut self) {
+        self.new_fonts.extend(self.font_handles.keys().cloned());
+    }
+
+    /// Get the handle for the given font name
+    pub fn get_handle(&self, id: String) -> Option<Handle<KayakFont>> {
+        self.font_handles.get(&id).cloned()
+    }
+
+    /// Get the font name for the given handle
+    pub fn get(&self, font: &Handle<KayakFont>) -> Option<String> {
+        self.font_ids.get(font).cloned()
+    }
+
+    /// Forces any text render commands to use subpixel font rendering for this specific font asset.
+    pub fn force_subpixel(&mut self, font: &Handle<KayakFont>) {
+        self.subpixel.insert(font.clone_weak());
+    }
+
+    /// Turns off the forced subpixel rendering mode for this font.
+    pub fn disable_subpixel(&mut self, font: &Handle<KayakFont>) {
+        self.subpixel.remove(font);
+    }
+
+    pub fn get_subpixel_forced(&self, font: &Handle<KayakFont>) -> bool {
+        self.subpixel.contains(font)
+    }
+
+    // pub(crate) fn add_loaded_to_kayak(
+    //     &mut self,
+    //     fonts: &Res<Assets<KayakFont>>,
+    //     context: &Context,
+    // ) {
+    //     if let Ok(mut kayak_context) = context.kayak_context.write() {
+    //         let new_fonts = self.new_fonts.drain(..).collect::<Vec<_>>();
+    //         for font_key in new_fonts {
+    //             let font_handle = self.font_handles.get(&font_key).unwrap();
+    //             if let Some(font) = fonts.get(font_handle) {
+    //                 kayak_context.set_asset(font_key, font.clone());
+    //             } else {
+    //                 self.new_fonts.push(font_key);
+    //             }
+    //         }
+    //     }
+    // }
+}
diff --git a/src/render/unified/pipeline.rs b/src/render/unified/pipeline.rs
index d8a71309ee8d32b1f574106dfd0a401a57f3d9a2..4d5aa330e4da2f9191d55a252257e925a26ee7c8 100644
--- a/src/render/unified/pipeline.rs
+++ b/src/render/unified/pipeline.rs
@@ -333,6 +333,7 @@ pub struct ExtractQuadBundle {
 pub enum UIQuadType {
     Quad,
     Text,
+    TextSubpixel,
     Image,
     Clip,
 }
@@ -416,18 +417,24 @@ pub fn prepare_quads(
         _padding_2: 0,
         _padding_3: 0,
     });
-    let text_type_offset = sprite_meta.types_buffer.push(QuadType {
+    let text_sub_pixel_type_offset = sprite_meta.types_buffer.push(QuadType {
         t: 1,
         _padding_1: 0,
         _padding_2: 0,
         _padding_3: 0,
     });
-    let image_type_offset = sprite_meta.types_buffer.push(QuadType {
+    let text_type_offset = sprite_meta.types_buffer.push(QuadType {
         t: 2,
         _padding_1: 0,
         _padding_2: 0,
         _padding_3: 0,
     });
+    let image_type_offset = sprite_meta.types_buffer.push(QuadType {
+        t: 3,
+        _padding_1: 0,
+        _padding_2: 0,
+        _padding_3: 0,
+    });
 
     sprite_meta
         .types_buffer
@@ -450,6 +457,7 @@ pub fn prepare_quads(
         match extracted_sprite.quad_type {
             UIQuadType::Quad => extracted_sprite.type_index = quad_type_offset,
             UIQuadType::Text => extracted_sprite.type_index = text_type_offset,
+            UIQuadType::TextSubpixel => extracted_sprite.type_index = text_sub_pixel_type_offset,
             UIQuadType::Image => extracted_sprite.type_index = image_type_offset,
             UIQuadType::Clip => {}
         };
diff --git a/src/render/unified/shader.wgsl b/src/render/unified/shader.wgsl
index d68144ae03072c5a50b2d0f19db093084ecd4d3e..5bf6e13a5bc6b5263852574fab31919198f1e19a 100644
--- a/src/render/unified/shader.wgsl
+++ b/src/render/unified/shader.wgsl
@@ -110,6 +110,16 @@ fn fragment(in: VertexOutput) -> @location(0) vec4<f32> {
         return vec4(red * in.color.r, green * in.color.g, blue * in.color.b, alpha);
     }
     if quad_type.t == 2 {
+        var px_range = 2.5;
+        var tex_dimensions = textureDimensions(font_texture);
+        var msdf_unit = vec2<f32>(px_range, px_range) / vec2<f32>(f32(tex_dimensions.x), f32(tex_dimensions.y));
+        var x = textureSample(font_texture, font_sampler, vec2<f32>(in.uv.x, 1.0 - in.uv.y), i32(in.uv.z));
+        var v = max(min(x.r, x.g), min(max(x.r, x.g), x.b));
+        var sig_dist = (v - 0.5) * dot(msdf_unit, 0.5 / fwidth(vec2<f32>(in.uv.x, 1.0 - in.uv.y)));
+        var a = clamp(sig_dist + 0.5, 0.0, 1.0);
+        return vec4<f32>(in.color.rgb, a);
+    }
+    if quad_type.t == 3 {
         var bs = min(in.border_radius, min(in.size.x, in.size.y));
         var mask = sdRoundBox(
             in.pos.xy * 2.0 - (in.size.xy),
diff --git a/src/render_primitive.rs b/src/render_primitive.rs
index 9c3fa2d6820b8bc656199be97f0ffffe9d9e30aa..89835a8ea481c7e38c440a42e830e3870efeb2fc 100644
--- a/src/render_primitive.rs
+++ b/src/render_primitive.rs
@@ -29,6 +29,7 @@ pub enum RenderPrimitive {
         layout: Rect,
         properties: TextProperties,
         word_wrap: bool,
+        subpixel: bool,
     },
     Image {
         border_radius: Corner<f32>,
@@ -111,6 +112,7 @@ impl From<&KStyle> for RenderPrimitive {
                 content,
                 alignment,
                 word_wrap,
+                subpixel,
             } => Self::Text {
                 color: style.color.resolve(),
                 content,
@@ -124,6 +126,7 @@ impl From<&KStyle> for RenderPrimitive {
                     ..Default::default()
                 },
                 word_wrap,
+                subpixel,
             },
             RenderCommand::Image { handle } => Self::Image {
                 border_radius: style.border_radius.resolve(),
diff --git a/src/styles/render_command.rs b/src/styles/render_command.rs
index d5a2630214bf9738b15858a23017f631f7c9110c..7583648da8fbec097b789cfe9dc5f591f3948c02 100644
--- a/src/styles/render_command.rs
+++ b/src/styles/render_command.rs
@@ -17,6 +17,7 @@ pub enum RenderCommand {
         content: String,
         alignment: Alignment,
         word_wrap: bool,
+        subpixel: bool,
     },
     Image {
         handle: Handle<Image>,
diff --git a/src/widgets/text.rs b/src/widgets/text.rs
index db20b9ca4b22c7a4ac7c14d528f1984b2319c274..6de78c40f61be608c9e8122cdbd801e83d27ac74 100644
--- a/src/widgets/text.rs
+++ b/src/widgets/text.rs
@@ -31,6 +31,8 @@ pub struct TextProps {
     /// Basic word wrapping.
     /// Defautls to true
     pub word_wrap: bool,
+    /// Enables subpixel rendering of text. This is useful on smaller low-dpi screens.
+    pub subpixel: bool,
 }
 
 impl Default for TextProps {
@@ -43,6 +45,7 @@ impl Default for TextProps {
             size: -1.0,
             alignment: Alignment::Start,
             word_wrap: true,
+            subpixel: false,
         }
     }
 }
@@ -82,6 +85,7 @@ pub fn text_render(
                     content: text.content.clone(),
                     alignment: text.alignment,
                     word_wrap: text.word_wrap,
+                    subpixel: text.subpixel,
                 }),
                 font: if let Some(ref font) = text.font {
                     StyleProp::Value(font.clone())