diff --git a/Cargo.lock b/Cargo.lock
index d3b708d5cf91e6b6c7e6307d8772a58bf627cc5f..91cc95ecef83d8f44bf15749d14e39b793495a37 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1898,6 +1898,7 @@ dependencies = [
 name = "kayak_font"
 version = "0.1.0"
 dependencies = [
+ "anyhow",
  "bevy",
  "serde",
  "serde_json",
diff --git a/assets/roboto.json.old b/assets/roboto.json.old
deleted file mode 100644
index 875af49e5db852469951736f27ca09844079dbcb..0000000000000000000000000000000000000000
--- a/assets/roboto.json.old
+++ /dev/null
@@ -1 +0,0 @@
-{"atlas":{"type":"msdf","distanceRange":2,"size":151.375,"width":1024,"height":720,"yOrigin":"bottom"},"metrics":{"emSize":1,"lineHeight":1.171875,"ascender":0.927734375,"descender":-0.244140625,"underlineY":-0.09765625,"underlineThickness":0.048828125},"glyphs":[{"unicode":32,"advance":0.24755859375},{"unicode":33,"advance":0.25732421875,"planeBounds":{"left":0.06883374567764243,"bottom":-0.013855938080615242,"right":0.19434984807235753,"top":0.71942234433061514},"atlasBounds":{"left":697.5,"bottom":331.5,"right":716.5,"top":442.5}},{"unicode":34,"advance":0.31982421875,"planeBounds":{"left":0.057747221111168451,"bottom":0.50057537352910819,"right":0.27574887263883152,"top":0.75821368897089192},"atlasBounds":{"left":899.5,"bottom":178.5,"right":932.5,"top":217.5}},{"unicode":35,"advance":0.61572265625,"planeBounds":{"left":0.049806703524979304,"bottom":-0.0078673358794385286,"right":0.6113261089750206,"top":0.71880483587943844},"atlasBounds":{"left":858.5,"bottom":332.5,"right":943.5,"top":442.5}},{"unicode":36,"advance":0.5615234375,"planeBounds":{"left":0.046488931216453339,"bottom":-0.11003222414327002,"right":0.51552278753354663,"top":0.83464159914326996},"atlasBounds":{"left":278.5,"bottom":576.5,"right":349.5,"top":719.5}},{"unicode":37,"advance":0.732421875,"planeBounds":{"left":0.043114710208505325,"bottom":-0.017776501857968668,"right":0.69711966479149456,"top":0.72871400185796853},"atlasBounds":{"left":438.5,"bottom":446.5,"right":537.5,"top":559.5}},{"unicode":38,"advance":0.62158203125,"planeBounds":{"left":0.040012450970272451,"bottom":-0.017776501857968668,"right":0.62795629902972738,"top":0.72871400185796853},"atlasBounds":{"left":538.5,"bottom":446.5,"right":627.5,"top":559.5}},{"unicode":39,"advance":0.17431640625,"planeBounds":{"left":0.040671287933526007,"bottom":0.50754053823028489,"right":0.13315683706647397,"top":0.75857274301971511},"atlasBounds":{"left":994.5,"bottom":27.5,"right":1008.5,"top":65.5}},{"unicode":40,"advance":0.341796875,"planeBounds":{"left":0.055119332550578062,"bottom":-0.23880884373709743,"right":0.33257597994942201,"top":0.81156274998709732},"atlasBounds":{"left":0.5,"bottom":560.5,"right":42.5,"top":719.5}},{"unicode":41,"advance":0.34765625,"planeBounds":{"left":0.0089767544255780589,"bottom":-0.23880884373709743,"right":0.28643340182442201,"top":0.81156274998709732},"atlasBounds":{"left":43.5,"bottom":560.5,"right":85.5,"top":719.5}},{"unicode":42,"advance":0.4306640625,"planeBounds":{"left":0.0069954050758670762,"bottom":0.2894517944235136,"right":0.42318037617413296,"top":0.71884898682648635},"atlasBounds":{"left":713.5,"bottom":152.5,"right":776.5,"top":217.5}},{"unicode":43,"advance":0.56689453125,"planeBounds":{"left":0.03070607646056973,"bottom":0.062530643579686171,"right":0.53277048603943011,"top":0.5976256064203137},"atlasBounds":{"left":505.5,"bottom":136.5,"right":581.5,"top":217.5}},{"unicode":44,"advance":0.1962890625,"planeBounds":{"left":0.0063051181229355786,"bottom":-0.14945619742206853,"right":0.15824566312706437,"top":0.1147882286720685},"atlasBounds":{"left":994.5,"bottom":66.5,"right":1017.5,"top":106.5}},{"unicode":45,"advance":0.27587890625,"planeBounds":{"left":0.0083878735291081988,"bottom":0.25575917855852598,"right":0.26602618897089186,"top":0.34824472769147397},"atlasBounds":{"left":144.5,"bottom":6.5,"right":183.5,"top":20.5}},{"unicode":46,"advance":0.26318359375,"planeBounds":{"left":0.06040373722646572,"bottom":-0.014418207447357567,"right":0.19252595027353425,"top":0.11109789494735753},"atlasBounds":{"left":472.5,"bottom":115.5,"right":492.5,"top":134.5}},{"unicode":47,"advance":0.412109375,"planeBounds":{"left":0.00092051700557392938,"bottom":-0.068112411940028941,"right":0.39068104549442606,"top":0.71801475569002882},"atlasBounds":{"left":578.5,"bottom":600.5,"right":637.5,"top":719.5}},{"unicode":48,"advance":0.5615234375,"planeBounds":{"left":0.049303705292630062,"bottom":-0.017776501857968668,"right":0.51173145095736994,"top":0.72871400185796853},"atlasBounds":{"left":628.5,"bottom":446.5,"right":698.5,"top":559.5}},{"unicode":49,"advance":0.5615234375,"planeBounds":{"left":0.074147987523224629,"bottom":-0.0094614068306152418,"right":0.3648168562267754,"top":0.72381687558061514},"atlasBounds":{"left":717.5,"bottom":331.5,"right":761.5,"top":442.5}},{"unicode":50,"advance":0.5615234375,"planeBounds":{"left":0.037427100536746485,"bottom":-0.0095906340317919551,"right":0.53288539946325342,"top":0.73029375903179183},"atlasBounds":{"left":172.5,"bottom":330.5,"right":247.5,"top":442.5}},{"unicode":51,"advance":0.5615234375,"planeBounds":{"left":0.036723306216453339,"bottom":-0.017776501857968668,"right":0.50575716253354663,"top":0.72871400185796853},"atlasBounds":{"left":699.5,"bottom":446.5,"right":770.5,"top":559.5}},{"unicode":52,"advance":0.5615234375,"planeBounds":{"left":0.018226277030862877,"bottom":-0.0078673358794385286,"right":0.54671512921913701,"top":0.71880483587943844},"atlasBounds":{"left":602.5,"bottom":218.5,"right":682.5,"top":328.5}},{"unicode":53,"advance":0.5615234375,"planeBounds":{"left":0.067370111542630021,"bottom":-0.019356259031791955,"right":0.52979785720736983,"top":0.72052813403179183},"atlasBounds":{"left":248.5,"bottom":330.5,"right":318.5,"top":442.5}},{"unicode":54,"advance":0.5615234375,"planeBounds":{"left":0.054545571841453339,"bottom":-0.019112118406791955,"right":0.52357942815854663,"top":0.72077227465679183},"atlasBounds":{"left":319.5,"bottom":330.5,"right":390.5,"top":442.5}},{"unicode":55,"advance":0.5615234375,"planeBounds":{"left":0.030102881786746489,"bottom":-0.0078673358794385286,"right":0.52556118071325342,"top":0.71880483587943844},"atlasBounds":{"left":944.5,"bottom":332.5,"right":1019.5,"top":442.5}},{"unicode":56,"advance":0.5615234375,"planeBounds":{"left":0.046244790591453339,"bottom":-0.017776501857968668,"right":0.51527864690854663,"top":0.72871400185796853},"atlasBounds":{"left":771.5,"bottom":446.5,"right":842.5,"top":559.5}},{"unicode":57,"advance":0.5615234375,"planeBounds":{"left":0.041247064667630062,"bottom":-0.0098347746567919551,"right":0.50367481033236994,"top":0.73004961840679183},"atlasBounds":{"left":411.5,"bottom":330.5,"right":481.5,"top":442.5}},{"unicode":58,"advance":0.2421875,"planeBounds":{"left":0.05576506535146572,"bottom":-0.013540631773843976,"right":0.18788727839853425,"top":0.54137266302384379},"atlasBounds":{"left":428.5,"bottom":22.5,"right":448.5,"top":106.5}},{"unicode":59,"advance":0.21142578125,"planeBounds":{"left":0.010685335595582152,"bottom":-0.15102002799855493,"right":0.17583810190441782,"top":0.54262159049855485},"atlasBounds":{"left":0.5,"bottom":1.5,"right":25.5,"top":106.5}},{"unicode":60,"advance":0.50830078125,"planeBounds":{"left":0.026770795700867076,"bottom":0.08776301061880673,"right":0.44295576679913296,"top":0.54358464563119313},"atlasBounds":{"left":582.5,"bottom":148.5,"right":645.5,"top":217.5}},{"unicode":61,"advance":0.548828125,"planeBounds":{"left":0.066436490374690363,"bottom":0.18681172907204793,"right":0.48922757212530965,"top":0.48408670842795209},"atlasBounds":{"left":834.5,"bottom":172.5,"right":898.5,"top":217.5}},{"unicode":62,"advance":0.5224609375,"planeBounds":{"left":0.055924129722336915,"bottom":0.08825129186880673,"right":0.4919274327776631,"top":0.54407292688119313},"atlasBounds":{"left":646.5,"bottom":148.5,"right":712.5,"top":217.5}},{"unicode":63,"advance":0.47216796875,"planeBounds":{"left":0.029829710402043789,"bottom":-0.012276180906791955,"right":0.43940857084795626,"top":0.72760821215679183},"atlasBounds":{"left":482.5,"bottom":330.5,"right":544.5,"top":442.5}},{"unicode":64,"advance":0.89794921875,"planeBounds":{"left":0.05015928215963629,"bottom":-0.2302643250670933,"right":0.85610478174675431,"top":0.70780338756709327},"atlasBounds":{"left":350.5,"bottom":577.5,"right":472.5,"top":719.5}},{"unicode":65,"advance":0.65234375,"planeBounds":{"left":0.0060196489858587478,"bottom":-0.0078673358794385286,"right":0.64681238226414106,"top":0.71880483587943844},"atlasBounds":{"left":250.5,"bottom":107.5,"right":347.5,"top":217.5}},{"unicode":66,"advance":0.62255859375,"planeBounds":{"left":0.073430685835569734,"bottom":-0.0078673358794385286,"right":0.57549509541443022,"top":0.71880483587943844},"atlasBounds":{"left":173.5,"bottom":107.5,"right":249.5,"top":217.5}},{"unicode":67,"advance":0.65087890625,"planeBounds":{"left":0.051027406649979304,"bottom":-0.017776501857968668,"right":0.6125468121000206,"top":0.72871400185796853},"atlasBounds":{"left":843.5,"bottom":446.5,"right":928.5,"top":559.5}},{"unicode":68,"advance":0.65576171875,"planeBounds":{"left":0.075355183280862881,"bottom":-0.0078673358794385286,"right":0.60384403546913701,"top":0.71880483587943844},"atlasBounds":{"left":0.5,"bottom":107.5,"right":80.5,"top":217.5}},{"unicode":69,"advance":0.568359375,"planeBounds":{"left":0.073832681216453297,"bottom":-0.0078673358794385286,"right":0.54286653753354663,"top":0.71880483587943844},"atlasBounds":{"left":939.5,"bottom":218.5,"right":1010.5,"top":328.5}},{"unicode":70,"advance":0.552734375,"planeBounds":{"left":0.07482355749380673,"bottom":-0.0078673358794385286,"right":0.53064519250619313,"top":0.71880483587943844},"atlasBounds":{"left":869.5,"bottom":218.5,"right":938.5,"top":328.5}},{"unicode":71,"advance":0.68115234375,"planeBounds":{"left":0.052736391024979304,"bottom":-0.017776501857968668,"right":0.6142557964750206,"top":0.72871400185796853},"atlasBounds":{"left":929.5,"bottom":446.5,"right":1014.5,"top":559.5}},{"unicode":72,"advance":0.712890625,"planeBounds":{"left":0.074953187899979318,"bottom":-0.0078673358794385286,"right":0.6364725933500206,"top":0.71880483587943844},"atlasBounds":{"left":701.5,"bottom":218.5,"right":786.5,"top":328.5}},{"unicode":73,"advance":0.27197265625,"planeBounds":{"left":0.080078528204995864,"bottom":-0.0078673358794385286,"right":0.19238240929500411,"top":0.71880483587943844},"atlasBounds":{"left":683.5,"bottom":218.5,"right":700.5,"top":328.5}},{"unicode":74,"advance":0.5517578125,"planeBounds":{"left":0.019030267792630059,"bottom":-0.019356259031791955,"right":0.48145801345736994,"top":0.72052813403179183},"atlasBounds":{"left":545.5,"bottom":330.5,"right":615.5,"top":442.5}},{"unicode":75,"advance":0.626953125,"planeBounds":{"left":0.074220766024979318,"bottom":-0.0078673358794385286,"right":0.6357401714750206,"top":0.71880483587943844},"atlasBounds":{"left":516.5,"bottom":218.5,"right":601.5,"top":328.5}},{"unicode":76,"advance":0.5380859375,"planeBounds":{"left":0.07348794094498344,"bottom":-0.0078673358794385286,"right":0.52270346530501643,"top":0.71880483587943844},"atlasBounds":{"left":447.5,"bottom":218.5,"right":515.5,"top":328.5}},{"unicode":77,"advance":0.873046875,"planeBounds":{"left":0.072943210995561475,"bottom":-0.0078673358794385286,"right":0.79961538275443844,"top":0.71880483587943844},"atlasBounds":{"left":336.5,"bottom":218.5,"right":446.5,"top":328.5}},{"unicode":78,"advance":0.712890625,"planeBounds":{"left":0.074953187899979318,"bottom":-0.0078673358794385286,"right":0.6364725933500206,"top":0.71880483587943844},"atlasBounds":{"left":250.5,"bottom":218.5,"right":335.5,"top":328.5}},{"unicode":79,"advance":0.6875,"planeBounds":{"left":0.049533935345272451,"bottom":-0.017776501857968668,"right":0.63747778340472738,"top":0.72871400185796853},"atlasBounds":{"left":0.5,"bottom":329.5,"right":89.5,"top":442.5}},{"unicode":80,"advance":0.630859375,"planeBounds":{"left":0.073890339530862881,"bottom":-0.0078673358794385286,"right":0.60237919171913701,"top":0.71880483587943844},"atlasBounds":{"left":82.5,"bottom":218.5,"right":162.5,"top":328.5}},{"unicode":81,"advance":0.6875,"planeBounds":{"left":0.046604247845272451,"bottom":-0.12910422365297272,"right":0.63454809590472738,"top":0.72969016115297269},"atlasBounds":{"left":473.5,"bottom":589.5,"right":562.5,"top":719.5}},{"unicode":82,"advance":0.61572265625,"planeBounds":{"left":0.072540409204686171,"bottom":-0.0078673358794385286,"right":0.6076353720453137,"top":0.71880483587943844},"atlasBounds":{"left":0.5,"bottom":218.5,"right":81.5,"top":328.5}},{"unicode":83,"advance":0.59326171875,"planeBounds":{"left":0.029815799829686164,"bottom":-0.017776501857968668,"right":0.5649107626703137,"top":0.72871400185796853},"atlasBounds":{"left":90.5,"bottom":329.5,"right":171.5,"top":442.5}},{"unicode":84,"advance":0.5966796875,"planeBounds":{"left":0.014765366948802596,"bottom":-0.0078673358794385286,"right":0.58289088305119729,"top":0.71880483587943844},"atlasBounds":{"left":163.5,"bottom":218.5,"right":249.5,"top":328.5}},{"unicode":85,"advance":0.6484375,"planeBounds":{"left":0.061439167655862881,"bottom":-0.019356259031791955,"right":0.58992801984413701,"top":0.72052813403179183},"atlasBounds":{"left":616.5,"bottom":330.5,"right":696.5,"top":442.5}},{"unicode":86,"advance":0.63623046875,"planeBounds":{"left":0.0048132596382121744,"bottom":-0.0078673358794385286,"right":0.63239377161178767,"top":0.71880483587943844},"atlasBounds":{"left":762.5,"bottom":332.5,"right":857.5,"top":442.5}},{"unicode":87,"advance":0.88720703125,"planeBounds":{"left":0.021659769173203964,"bottom":-0.0078673358794385286,"right":0.87384804332679589,"top":0.71880483587943844},"atlasBounds":{"left":893.5,"bottom":609.5,"right":1022.5,"top":719.5}},{"unicode":88,"advance":0.626953125,"planeBounds":{"left":0.020237060345272454,"bottom":-0.0078673358794385286,"right":0.60818090840472738,"top":0.71880483587943844},"atlasBounds":{"left":348.5,"bottom":107.5,"right":437.5,"top":217.5}},{"unicode":89,"advance":0.6005859375,"planeBounds":{"left":-0.0012616284320809716,"bottom":-0.0078673358794385286,"right":0.59989444093208089,"top":0.71880483587943844},"atlasBounds":{"left":81.5,"bottom":107.5,"right":172.5,"top":217.5}},{"unicode":90,"advance":0.5986328125,"planeBounds":{"left":0.033233768579686164,"bottom":-0.0078673358794385286,"right":0.5683287314203137,"top":0.71880483587943844},"atlasBounds":{"left":787.5,"bottom":218.5,"right":868.5,"top":328.5}},{"unicode":91,"advance":0.26513671875,"planeBounds":{"left":0.064238418339698608,"bottom":-0.16207711860033028,"right":0.26242173791030143,"top":0.82223336860033025},"atlasBounds":{"left":86.5,"bottom":570.5,"right":116.5,"top":719.5}},{"unicode":92,"advance":0.41015625,"planeBounds":{"left":0.012754180429397216,"bottom":-0.068112411940028941,"right":0.40912081957060281,"top":0.71801475569002882},"atlasBounds":{"left":638.5,"bottom":600.5,"right":698.5,"top":719.5}},{"unicode":93,"advance":0.26513671875,"planeBounds":{"left":-0.0024119722853014154,"bottom":-0.16207711860033028,"right":0.19577134728530138,"top":0.82223336860033025},"atlasBounds":{"left":117.5,"bottom":570.5,"right":147.5,"top":719.5}},{"unicode":94,"advance":0.41796875,"planeBounds":{"left":0.023036714234104069,"bottom":0.34847616735910403,"right":0.39297891076589592,"top":0.71841836389089586},"atlasBounds":{"left":777.5,"bottom":161.5,"right":833.5,"top":217.5}},{"unicode":95,"advance":0.451171875,"planeBounds":{"left":-0.0056279353323699412,"bottom":-0.083108008941473993,"right":0.45679981033236994,"top":0.0093775401914739792},"atlasBounds":{"left":73.5,"bottom":6.5,"right":143.5,"top":20.5}},{"unicode":96,"advance":0.30908203125,"planeBounds":{"left":0.020637846111168454,"bottom":0.59894932842175885,"right":0.23863949763883152,"top":0.75749598407824115},"atlasBounds":{"left":438.5,"bottom":110.5,"right":471.5,"top":134.5}},{"unicode":97,"advance":0.5439453125,"planeBounds":{"left":0.043329416868806772,"bottom":-0.016599546475020693,"right":0.49915105188119319,"top":0.5449198589750206},"atlasBounds":{"left":358.5,"bottom":21.5,"right":427.5,"top":106.5}},{"unicode":98,"advance":0.56103515625,"planeBounds":{"left":0.060778314667630062,"bottom":-0.019643340988852235,"right":0.52320606033236994,"top":0.75987771598885212},"atlasBounds":{"left":699.5,"bottom":601.5,"right":769.5,"top":719.5}},{"unicode":99,"advance":0.5234375,"planeBounds":{"left":0.036364252167630062,"bottom":-0.016599546475020693,"right":0.49879199783236994,"top":0.5449198589750206},"atlasBounds":{"left":287.5,"bottom":21.5,"right":357.5,"top":106.5}},{"unicode":100,"advance":0.56396484375,"planeBounds":{"left":0.038073236542630062,"bottom":-0.019643340988852235,"right":0.50050098220736994,"top":0.75987771598885212},"atlasBounds":{"left":770.5,"bottom":601.5,"right":840.5,"top":719.5}},{"unicode":101,"advance":0.52978515625,"planeBounds":{"left":0.038317377167630062,"bottom":-0.016599546475020693,"right":0.50074512283236994,"top":0.5449198589750206},"atlasBounds":{"left":149.5,"bottom":21.5,"right":219.5,"top":106.5}},{"unicode":102,"advance":0.34716796875,"planeBounds":{"left":0.020509022114987636,"bottom":-0.0096335753638522349,"right":0.35742066538501238,"top":0.76988748161385212},"atlasBounds":{"left":841.5,"bottom":601.5,"right":892.5,"top":719.5}},{"unicode":103,"advance":0.56103515625,"planeBounds":{"left":0.038805658417630062,"bottom":-0.21481230001032209,"right":0.50123340408236994,"top":0.54489042501032192},"atlasBounds":{"left":367.5,"bottom":444.5,"right":437.5,"top":559.5}},{"unicode":104,"advance":0.55078125,"planeBounds":{"left":0.061424450673513625,"bottom":-0.0081544178364988083,"right":0.49082164307648635,"top":0.75815441783649862},"atlasBounds":{"left":0.5,"bottom":443.5,"right":65.5,"top":559.5}},{"unicode":105,"advance":0.24267578125,"planeBounds":{"left":0.05955640192764243,"bottom":-0.0095906340317919551,"right":0.18507250432235753,"top":0.73029375903179183},"atlasBounds":{"left":391.5,"bottom":330.5,"right":410.5,"top":442.5}},{"unicode":106,"advance":0.23876953125,"planeBounds":{"left":-0.040641450763831549,"bottom":-0.22197785759444671,"right":0.17736020076383152,"top":0.72930207634444666},"atlasBounds":{"left":244.5,"bottom":575.5,"right":277.5,"top":719.5}},{"unicode":107,"advance":0.5068359375,"planeBounds":{"left":0.059442698118806772,"bottom":-0.0081544178364988083,"right":0.51526433313119313,"top":0.75815441783649862},"atlasBounds":{"left":66.5,"bottom":443.5,"right":135.5,"top":559.5}},{"unicode":108,"advance":0.24267578125,"planeBounds":{"left":0.068489005406172587,"bottom":-0.0081544178364988083,"right":0.17418677584382741,"top":0.75815441783649862},"atlasBounds":{"left":136.5,"bottom":443.5,"right":152.5,"top":559.5}},{"unicode":109,"advance":0.87646484375,"planeBounds":{"left":0.058381059364677898,"bottom":-0.0084136786488439776,"right":0.81808378438532192,"top":0.54649961614884379},"atlasBounds":{"left":515.5,"bottom":22.5,"right":630.5,"top":106.5}},{"unicode":110,"advance":0.5517578125,"planeBounds":{"left":0.061424450673513625,"bottom":-0.0084136786488439776,"right":0.49082164307648635,"top":0.54649961614884379},"atlasBounds":{"left":673.5,"bottom":22.5,"right":738.5,"top":106.5}},{"unicode":111,"advance":0.5703125,"planeBounds":{"left":0.037182959911746485,"bottom":-0.016599546475020693,"right":0.53264125883825342,"top":0.5449198589750206},"atlasBounds":{"left":73.5,"bottom":21.5,"right":148.5,"top":106.5}},{"unicode":112,"advance":0.56103515625,"planeBounds":{"left":0.060290033417630062,"bottom":-0.21237089376032209,"right":0.52271777908236994,"top":0.54733183126032192},"atlasBounds":{"left":296.5,"bottom":444.5,"right":366.5,"top":559.5}},{"unicode":113,"advance":0.568359375,"planeBounds":{"left":0.037829095917630062,"bottom":-0.21237089376032209,"right":0.50025684158236994,"top":0.54733183126032192},"atlasBounds":{"left":225.5,"bottom":444.5,"right":295.5,"top":559.5}},{"unicode":114,"advance":0.33837890625,"planeBounds":{"left":0.060619653501754772,"bottom":-0.0084136786488439776,"right":0.33147019024824526,"top":0.54649961614884379},"atlasBounds":{"left":631.5,"bottom":22.5,"right":672.5,"top":106.5}},{"unicode":115,"advance":0.515625,"planeBounds":{"left":0.038346004722336915,"bottom":-0.016599546475020693,"right":0.4743493077776631,"top":0.5449198589750206},"atlasBounds":{"left":220.5,"bottom":21.5,"right":286.5,"top":106.5}},{"unicode":116,"advance":0.32666015625,"planeBounds":{"left":-0.0037471856291287952,"bottom":-0.016972511096201532,"right":0.30013390437912885,"top":0.66345688609620135},"atlasBounds":{"left":26.5,"bottom":3.5,"right":72.5,"top":106.5}},{"unicode":117,"advance":0.55126953125,"planeBounds":{"left":0.059715466298513625,"bottom":-0.018179303648843976,"right":0.48911265870148635,"top":0.53673399114884379},"atlasBounds":{"left":449.5,"bottom":22.5,"right":514.5,"top":106.5}},{"unicode":118,"advance":0.484375,"planeBounds":{"left":0.0064498687164533447,"bottom":-0.006690380496490551,"right":0.47548372503354663,"top":0.5350106929964904},"atlasBounds":{"left":922.5,"bottom":24.5,"right":993.5,"top":106.5}},{"unicode":119,"advance":0.75146484375,"planeBounds":{"left":0.011175632870561471,"bottom":-0.006690380496490551,"right":0.73784780462943844,"top":0.5350106929964904},"atlasBounds":{"left":739.5,"bottom":24.5,"right":849.5,"top":106.5}},{"unicode":120,"advance":0.49560546875,"planeBounds":{"left":0.012309243716453346,"bottom":-0.006690380496490551,"right":0.48134310003354663,"top":0.5350106929964904},"atlasBounds":{"left":850.5,"bottom":24.5,"right":921.5,"top":106.5}},{"unicode":121,"advance":0.47314453125,"planeBounds":{"left":0.0013229155914533447,"bottom":-0.22238065938532209,"right":0.47035677190854663,"top":0.53732206563532192},"atlasBounds":{"left":153.5,"bottom":444.5,"right":224.5,"top":559.5}},{"unicode":122,"advance":0.49560546875,"planeBounds":{"left":0.034683895347336915,"bottom":-0.006690380496490551,"right":0.4706871984026631,"top":0.5350106929964904},"atlasBounds":{"left":438.5,"bottom":135.5,"right":504.5,"top":217.5}},{"unicode":123,"advance":0.33837890625,"planeBounds":{"left":0.023955618419694489,"bottom":-0.18831507889915358,"right":0.33444281908030554,"top":0.78938929764915344},"atlasBounds":{"left":196.5,"bottom":571.5,"right":243.5,"top":719.5}},{"unicode":124,"advance":0.24365234375,"planeBounds":{"left":0.075583397308526007,"bottom":-0.13984641115297272,"right":0.16806894644147397,"top":0.71894797365297269},"atlasBounds":{"left":563.5,"bottom":589.5,"right":577.5,"top":719.5}},{"unicode":125,"advance":0.33837890625,"planeBounds":{"left":0.0022271027946944911,"bottom":-0.18831507889915358,"right":0.31271430345530554,"top":0.78938929764915344},"atlasBounds":{"left":148.5,"bottom":571.5,"right":195.5,"top":719.5}},{"unicode":126,"advance":0.68017578125,"planeBounds":{"left":0.056269273198802594,"bottom":0.1882475420623452,"right":0.62439478930119729,"top":0.39964308293765483},"atlasBounds":{"left":933.5,"bottom":185.5,"right":1019.5,"top":217.5}}],"kerning":[]}
diff --git a/assets/roboto.json b/assets/roboto.kayak_font
similarity index 100%
rename from assets/roboto.json
rename to assets/roboto.kayak_font
diff --git a/assets/roboto.png.old b/assets/roboto.png.old
deleted file mode 100644
index 7f4e0f9128bb32bb381d5ae42d21ed7cc8177e5a..0000000000000000000000000000000000000000
Binary files a/assets/roboto.png.old and /dev/null differ
diff --git a/bevy_kayak_ui/src/lib.rs b/bevy_kayak_ui/src/lib.rs
index 1f1d45ff7c38e3add19967a1d7e743095733ed21..ddc7232708b49d8c924eb4628ef4154368b669f2 100644
--- a/bevy_kayak_ui/src/lib.rs
+++ b/bevy_kayak_ui/src/lib.rs
@@ -13,6 +13,7 @@ mod render;
 pub use bevy_context::BevyContext;
 pub use camera::*;
 use kayak_core::InputEvent;
+pub use render::unified::font::FontMapping;
 pub use render::unified::image::ImageManager;
 
 #[derive(Default)]
diff --git a/bevy_kayak_ui/src/render/unified/font/extract.rs b/bevy_kayak_ui/src/render/unified/font/extract.rs
index feae2c49b39a6d9c38a1585a692b867e05552ed8..c4d7fbec89b1ee118d179263239c8c231bc155b5 100644
--- a/bevy_kayak_ui/src/render/unified/font/extract.rs
+++ b/bevy_kayak_ui/src/render/unified/font/extract.rs
@@ -44,7 +44,13 @@ pub fn extract_texts(
         };
 
         let font_handle = font_mapping.get_handle(font).unwrap();
-        let font = fonts.get(font_handle.clone()).unwrap();
+        let font = fonts.get(font_handle.clone());
+
+        if font.is_none() {
+            continue;
+        }
+
+        let font = font.unwrap();
 
         let chars_layouts =
             font.get_layout(Vec2::new(layout.posx, layout.posy), content, font_size);
diff --git a/bevy_kayak_ui/src/render/unified/font/font_mapping.rs b/bevy_kayak_ui/src/render/unified/font/font_mapping.rs
index 743eedd5436a3e61d1e692e0f03c9b857b9f815c..2559243f9c5ce418e093cee72a515ac518c10d88 100644
--- a/bevy_kayak_ui/src/render/unified/font/font_mapping.rs
+++ b/bevy_kayak_ui/src/render/unified/font/font_mapping.rs
@@ -18,7 +18,7 @@ impl Default for FontMapping {
 }
 
 impl FontMapping {
-    pub(crate) fn add(&mut self, handle: Handle<KayakFont>) -> u16 {
+    pub fn add(&mut self, handle: Handle<KayakFont>) -> u16 {
         if !self.font_ids.contains_key(&handle) {
             let id = self.count;
             self.font_ids.insert(handle.clone(), id);
@@ -31,7 +31,7 @@ impl FontMapping {
         }
     }
 
-    pub(crate) fn get_handle(&self, id: u16) -> Option<Handle<KayakFont>> {
+    pub fn get_handle(&self, id: u16) -> Option<Handle<KayakFont>> {
         self.font_handles
             .get(&id)
             .and_then(|item| Some(item.clone()))
diff --git a/bevy_kayak_ui/src/render/unified/font/mod.rs b/bevy_kayak_ui/src/render/unified/font/mod.rs
index f4cd0ba7c14701009e9d0d8d6d065876c542373e..82a6e943d8d8be0c3d2ef0409cc60df6c2c9fd2a 100644
--- a/bevy_kayak_ui/src/render/unified/font/mod.rs
+++ b/bevy_kayak_ui/src/render/unified/font/mod.rs
@@ -12,7 +12,7 @@ use bevy::{
     },
     utils::HashSet,
 };
-use kayak_font::{FontTextureCache, KayakFont, Sdf};
+use kayak_font::{FontTextureCache, KayakFont, KayakFontPlugin};
 
 mod extract;
 mod font_mapping;
@@ -26,132 +26,12 @@ pub struct TextRendererPlugin;
 
 impl Plugin for TextRendererPlugin {
     fn build(&self, app: &mut bevy::prelude::App) {
-        app.add_asset::<KayakFont>()
-            .init_resource::<FontMapping>()
-            .add_startup_system(load_fonts)
-            .add_system(set_font_texture);
+        app.add_plugin(KayakFontPlugin)
+            .init_resource::<FontMapping>();
 
         let render_app = app.sub_app(RenderApp);
         render_app.add_system_to_stage(RenderStage::Extract, extract_texts);
-
-        render_app
-            .init_resource::<FontTextureCache>()
-            .init_resource::<ExtractedFonts>()
-            .add_system_to_stage(RenderStage::Extract, extract_fonts)
-            .add_system_to_stage(RenderStage::Prepare, prepare_fonts)
-            .add_system_to_stage(RenderStage::Queue, create_and_update_font_cache_texture);
-    }
-}
-
-#[derive(Default)]
-pub struct ExtractedFonts {
-    pub fonts: Vec<(Handle<KayakFont>, KayakFont)>,
-}
-
-fn load_fonts(
-    mut font_assets: ResMut<Assets<KayakFont>>,
-    mut font_mapping: ResMut<FontMapping>,
-    asset_server: Res<AssetServer>,
-) {
-    let sdf = Sdf::from_string(include_str!("../../../../../assets/roboto.json").to_string());
-
-    let atlas_image: Handle<Image> = asset_server.load("roboto.png");
-
-    let mut font = KayakFont::new(sdf, atlas_image);
-    font.generate_char_ids();
-
-    let handle = font_assets.add(font);
-    font_mapping.add(handle);
-}
-
-pub fn set_font_texture(
-    mut texture_events: EventReader<AssetEvent<Image>>,
-    mut textures: ResMut<Assets<Image>>,
-    asset_server: Res<AssetServer>,
-) {
-    // quick and dirty, run this for all textures anytime a texture is created.
-    for event in texture_events.iter() {
-        match event {
-            AssetEvent::Created { handle } => {
-                if let Some(handle_path) = asset_server.get_handle_path(handle) {
-                    if handle_path.path().to_str().unwrap().contains("roboto") {
-                        if let Some(mut texture) = textures.get_mut(handle) {
-                            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;
-                        }
-                    }
-                }
-            }
-            _ => (),
-        }
-    }
-}
-
-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 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)
-            {
-                not_processed.push(handle);
-                continue;
-            }
-        } else {
-            not_processed.push(handle);
-            continue;
-        }
-
-        let font = font_asset.clone();
-        extracted_fonts.fonts.push((handle, font));
-    }
-
-    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);
+        render_app.add_system_to_stage(RenderStage::Queue, create_and_update_font_cache_texture);
     }
 }
 
diff --git a/examples/bevy.rs b/examples/bevy.rs
index 4895e2ad93c1d6f2513622bd4fc9766ed3ad913c..dc9c042deae3ec846a5b6af15b4de9f4bcc31f72 100644
--- a/examples/bevy.rs
+++ b/examples/bevy.rs
@@ -1,10 +1,10 @@
 use bevy::{
     math::Vec2,
-    prelude::{App as BevyApp, Commands, Res},
+    prelude::{App as BevyApp, AssetServer, Commands, Res, ResMut},
     window::{WindowDescriptor, Windows},
     PipelinedDefaultPlugins,
 };
-use bevy_kayak_ui::{BevyContext, BevyKayakUIPlugin, UICameraBundle};
+use bevy_kayak_ui::{BevyContext, BevyKayakUIPlugin, FontMapping, UICameraBundle};
 use kayak_components::Window;
 use kayak_core::Index;
 use kayak_ui::components::App;
@@ -28,9 +28,16 @@ fn TestState() {
     }
 }
 
-fn startup(mut commands: Commands, windows: Res<Windows>) {
+fn startup(
+    mut commands: Commands,
+    windows: Res<Windows>,
+    mut font_mapping: ResMut<FontMapping>,
+    asset_server: Res<AssetServer>,
+) {
     commands.spawn_bundle(UICameraBundle::new());
 
+    font_mapping.add(asset_server.load("roboto.kayak_font"));
+
     let window_size = if let Some(window) = windows.get_primary() {
         Vec2::new(window.width(), window.height())
     } else {
diff --git a/examples/counter.rs b/examples/counter.rs
index 2d946636fd5a0c8df3f5ea2a756004935f95e975..9f924cf90431730869a9bb1cbb636e7b3f85be01 100644
--- a/examples/counter.rs
+++ b/examples/counter.rs
@@ -1,10 +1,10 @@
 use bevy::{
     math::Vec2,
-    prelude::{App as BevyApp, Commands, Res},
+    prelude::{App as BevyApp, AssetServer, Commands, Res, ResMut},
     window::{WindowDescriptor, Windows},
     PipelinedDefaultPlugins,
 };
-use bevy_kayak_ui::{BevyContext, BevyKayakUIPlugin, UICameraBundle};
+use bevy_kayak_ui::{BevyContext, BevyKayakUIPlugin, FontMapping, UICameraBundle};
 use kayak_components::{Button, Text, Window};
 use kayak_core::{
     context::KayakContext,
@@ -50,9 +50,16 @@ fn Counter(context: &mut KayakContext) {
     }
 }
 
-fn startup(mut commands: Commands, windows: Res<Windows>) {
+fn startup(
+    mut commands: Commands,
+    windows: Res<Windows>,
+    mut font_mapping: ResMut<FontMapping>,
+    asset_server: Res<AssetServer>,
+) {
     commands.spawn_bundle(UICameraBundle::new());
 
+    font_mapping.add(asset_server.load("roboto.kayak_font"));
+
     let window_size = if let Some(window) = windows.get_primary() {
         Vec2::new(window.width(), window.height())
     } else {
diff --git a/examples/full_ui.rs b/examples/full_ui.rs
index 380f548b19ae54be19cd3bdd1ec069a05999745f..46eba9fd22d293499b1546f14a9ab7027c4c3a3e 100644
--- a/examples/full_ui.rs
+++ b/examples/full_ui.rs
@@ -4,7 +4,7 @@ use bevy::{
     window::{WindowDescriptor, Windows},
     PipelinedDefaultPlugins,
 };
-use bevy_kayak_ui::{BevyContext, BevyKayakUIPlugin, ImageManager, UICameraBundle};
+use bevy_kayak_ui::{BevyContext, BevyKayakUIPlugin, FontMapping, ImageManager, UICameraBundle};
 use kayak_components::{NinePatch, Text};
 use kayak_core::{
     context::KayakContext,
@@ -89,6 +89,7 @@ fn startup(
     windows: Res<Windows>,
     asset_server: Res<AssetServer>,
     mut image_manager: ResMut<ImageManager>,
+    mut font_mapping: ResMut<FontMapping>,
 ) {
     commands.spawn_bundle(UICameraBundle::new());
 
@@ -98,6 +99,8 @@ fn startup(
         panic!("Couldn't find primary window!");
     };
 
+    font_mapping.add(asset_server.load("roboto.kayak_font"));
+
     let handle: Handle<bevy::render2::texture::Image> = asset_server.load("kenny/panel_brown.png");
     let panel_brown_handle = image_manager.get(&handle);
 
diff --git a/kayak_font/Cargo.toml b/kayak_font/Cargo.toml
index f5ded9aef2a7f94113971945d39f0e85f8665262..4d787f23c6be0227b4cc403bdb348ca24e826b82 100644
--- a/kayak_font/Cargo.toml
+++ b/kayak_font/Cargo.toml
@@ -6,6 +6,7 @@ edition = "2021"
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
+anyhow = { version = "1.0" }
 bevy = { git = "https://github.com/bevyengine/bevy", rev = "38c7d5eb9e81ab8e1aec03673599b25a9aa0c69c" }
 serde = "1.0"
 serde_json = "1.0"
diff --git a/kayak_font/assets/roboto.json b/kayak_font/assets/roboto.kayak_font
similarity index 100%
rename from kayak_font/assets/roboto.json
rename to kayak_font/assets/roboto.kayak_font
diff --git a/kayak_font/examples/bevy.rs b/kayak_font/examples/bevy.rs
new file mode 100644
index 0000000000000000000000000000000000000000..1521e46c3380ad132d082f1d92c3734196c1de71
--- /dev/null
+++ b/kayak_font/examples/bevy.rs
@@ -0,0 +1,26 @@
+use bevy::{
+    prelude::{App as BevyApp, AssetServer, Commands, Handle, Res},
+    window::WindowDescriptor,
+    PipelinedDefaultPlugins,
+};
+use kayak_font::{KayakFont, KayakFontPlugin};
+
+fn startup(mut commands: Commands, asset_server: Res<AssetServer>) {
+    let font_handle: Handle<KayakFont> = asset_server.load("roboto.kayak_font");
+
+    dbg!(font_handle);
+}
+
+fn main() {
+    BevyApp::new()
+        .insert_resource(WindowDescriptor {
+            width: 1270.0,
+            height: 720.0,
+            title: String::from("UI Example"),
+            ..Default::default()
+        })
+        .add_plugins(PipelinedDefaultPlugins)
+        .add_plugin(KayakFontPlugin)
+        .add_startup_system(startup)
+        .run();
+}
diff --git a/kayak_font/src/font.rs b/kayak_font/src/font.rs
index 69a5802edc92e924bf57db97e804a8b7e5421da7..74e73d1e4148deec2aff3dcab76f9d8e1e63abd3 100644
--- a/kayak_font/src/font.rs
+++ b/kayak_font/src/font.rs
@@ -1,6 +1,12 @@
-use std::collections::HashMap;
+use std::{collections::HashMap, path::PathBuf};
 
-use bevy::{math::Vec2, prelude::Handle, reflect::TypeUuid, render2::texture::Image};
+use bevy::{
+    asset::{AssetLoader, AssetPath, BoxedFuture, LoadContext, LoadedAsset},
+    math::Vec2,
+    prelude::Handle,
+    reflect::TypeUuid,
+    render2::texture::Image,
+};
 
 use crate::Sdf;
 
@@ -76,3 +82,36 @@ impl KayakFont {
         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/lib.rs b/kayak_font/src/lib.rs
index 3ffc49344dfbaccbf3cc3b7d34e8d43d20cbc774..23a4753158832f1ef4148a11e4938607e5e8852d 100644
--- a/kayak_font/src/lib.rs
+++ b/kayak_font/src/lib.rs
@@ -6,9 +6,132 @@ mod renderer;
 mod sdf;
 
 pub use atlas::*;
+use bevy::{
+    prelude::{
+        AddAsset, AssetEvent, Assets, Commands, EventReader, Handle, Local, Plugin, Res, ResMut,
+    },
+    render2::{
+        render_resource::{FilterMode, TextureFormat, TextureUsages},
+        renderer::{RenderDevice, RenderQueue},
+        texture::Image,
+        RenderApp, RenderStage,
+    },
+    utils::HashSet,
+};
 pub use font::*;
 pub use glyph::*;
 pub use metrics::*;
 pub use sdf::*;
 
 pub use renderer::*;
+
+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);
+
+        let render_app = app.sub_app(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 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 } => {
+                if let Some(font) = fonts.get(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;
+                    }
+                }
+            }
+            _ => (),
+        }
+    }
+}
+
+#[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());
+                }
+            }
+        }
+    }
+
+    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)
+            {
+                not_processed.push(handle);
+                continue;
+            }
+        } else {
+            not_processed.push(handle);
+            continue;
+        }
+
+        let font = font_asset.clone();
+        extracted_fonts.fonts.push((handle, font));
+    }
+
+    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);
+    }
+}
diff --git a/kayak_font/src/sdf.rs b/kayak_font/src/sdf.rs
index 129efb18840e32148c6aa85973249ba62b5a6434..485e21398444669f41e9060575b43649917fccae 100644
--- a/kayak_font/src/sdf.rs
+++ b/kayak_font/src/sdf.rs
@@ -33,6 +33,21 @@ impl Sdf {
         value
     }
 
+    pub fn from_bytes(data: &[u8]) -> Sdf {
+        let value: Sdf = match serde_path_to_error::deserialize(
+            &mut serde_json::Deserializer::from_slice(&data),
+        ) {
+            Ok(v) => v,
+            Err(err) => {
+                let path = err.path().to_string();
+                dbg!(err);
+                panic!("failed to deserialize json! path: {}", path);
+            }
+        };
+
+        value
+    }
+
     pub fn max_glyph_size(&self) -> Vec2 {
         let mut size = Vec2::new(0.0, 0.0);
         self.glyphs.iter().for_each(|glyph| {
@@ -53,7 +68,7 @@ impl Sdf {
 
 #[test]
 fn test_sdf_loader() {
-    let sdf = Sdf::from_string(include_str!("../assets/roboto.json").to_string());
+    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.atlas.width == 212);
     assert!(sdf.atlas.height == 212);