aboutsummaryrefslogtreecommitdiff
path: root/internal/embedded
diff options
context:
space:
mode:
authorXe Iaso <me@xeiaso.net>2023-09-09 22:26:58 -0400
committerXe Iaso <me@xeiaso.net>2023-09-09 22:26:58 -0400
commit8876dc2340b5b04a8c72bb06e92693c35cb97a07 (patch)
tree0a87d95dd74c3f844b3e73ba33cd44b4a87f8aeb /internal/embedded
parent3c34d8854163880097bc6b326a6c79db7b5b3b73 (diff)
downloadxesite-8876dc2340b5b04a8c72bb06e92693c35cb97a07.tar.xz
xesite-8876dc2340b5b04a8c72bb06e92693c35cb97a07.zip
internal/embedded: move pregenned posts to JSON
Signed-off-by: Xe Iaso <me@xeiaso.net>
Diffstat (limited to 'internal/embedded')
-rw-r--r--internal/embedded/generate.go9
-rw-r--r--internal/embedded/posts.go8
-rw-r--r--internal/embedded/posts.gobbin5056681 -> 0 bytes
-rw-r--r--internal/embedded/posts.json6079
4 files changed, 6088 insertions, 8 deletions
diff --git a/internal/embedded/generate.go b/internal/embedded/generate.go
index 7cf0ae7..bb64200 100644
--- a/internal/embedded/generate.go
+++ b/internal/embedded/generate.go
@@ -4,7 +4,7 @@ package main
import (
"context"
- "encoding/gob"
+ "encoding/json"
"flag"
"log"
"os"
@@ -23,16 +23,17 @@ func main() {
}
slog.Info("loaded posts", "count", len(posts))
- fout, err := os.Create("posts.gob")
+ fout, err := os.Create("posts.json")
if err != nil {
log.Fatal(err)
}
defer fout.Close()
- enc := gob.NewEncoder(fout)
+ enc := json.NewEncoder(fout)
+ enc.SetIndent("", " ")
if err := enc.Encode(posts); err != nil {
log.Fatal(err)
}
- slog.Info("wrote posts.gob")
+ slog.Info("wrote posts.json")
}
diff --git a/internal/embedded/posts.go b/internal/embedded/posts.go
index 7390994..e4b7c67 100644
--- a/internal/embedded/posts.go
+++ b/internal/embedded/posts.go
@@ -3,7 +3,7 @@ package embedded
import (
"bytes"
_ "embed"
- "encoding/gob"
+ "encoding/json"
"xeiaso.net/v4/internal"
)
@@ -11,14 +11,14 @@ import (
//go:generate go run generate.go
var (
- //go:embed posts.gob
- postGob []byte
+ //go:embed posts.json
+ postJSON []byte
Posts []*internal.Post
)
func init() {
- if err := gob.NewDecoder(bytes.NewReader(postGob)).Decode(&Posts); err != nil {
+ if err := json.NewDecoder(bytes.NewReader(postJSON)).Decode(&Posts); err != nil {
panic(err)
}
}
diff --git a/internal/embedded/posts.gob b/internal/embedded/posts.gob
deleted file mode 100644
index 359bef2..0000000
--- a/internal/embedded/posts.gob
+++ /dev/null
Binary files differ
diff --git a/internal/embedded/posts.json b/internal/embedded/posts.json
new file mode 100644
index 0000000..7beab99
--- /dev/null
+++ b/internal/embedded/posts.json
@@ -0,0 +1,6079 @@
+[
+ {
+ "frontMatter": {
+ "title": "Tears of the Kingdom is perfect",
+ "date": "2023-09-11",
+ "tags": [
+ "totk",
+ "nintendoSwitch"
+ ]
+ },
+ "link": "blog/totk",
+ "bodyHTML": "\u003cp\u003e\u003cmeta property=\"og:image\" content=\"https://cdn.xeiaso.net/file/christine-static/hero/elf-princess-smol.png\"\u003e\u003cfigure class=\"hero\" style=\"margin:0\"\u003e\u003cpicture style=\"margin:0\"\u003e\u003csource type=\"image/avif\" srcset=\"https://cdn.xeiaso.net/file/christine-static/hero/elf-princess.avif\"\u003e\u003csource type=\"image/webp\" srcset=\"https://cdn.xeiaso.net/file/christine-static/hero/elf-princess.webp\"\u003e\u003cimg style=\"padding:0\" loading=\"lazy\" alt=\"hero image elf-princess\" src=\"https://cdn.xeiaso.net/file/christine-static/hero/elf-princess-smol.png\"\u003e\u003c/picture\u003e\u003cfigcaption\u003eLinge Claire -- 1girl, green hair, green eyes, smile, tshirt, skirt, desert, sand, very long hair, pointy ears, sword, oasis\u003c/figcaption\u003e\u003c/figure\u003e\u003c/p\u003e\n\u003cp\u003eAs people know, I'm a gamer. I play a lot of video games because it's a huge part of how I relax. I've been playing video games since my ability to remember started and it's been a huge part of my life. I'm also regularly accused of being a games journalist. I'm not, but I'm told that my opinions on games are a bit more \u0026quot;trustable\u0026quot; because I don't have quotas or anything to compel me to write about games I don't want to write about.\u003c/p\u003e\n\u003cp\u003eThe Legend of Zelda: Tears of the Kingdom is a perfect game. It's going to go down in history as one of the best games ever made. It's a game that I think is going to be a generational leap forward in open-world games, much like how Breath of the Wild is constantly copied in open-world games after it.\u003c/p\u003e\n\u003cp\u003eThe really annoying part about this game is that it's basically impossible to talk about this game without talking about its prequel, The Legend of Zelda: Breath of the Wild. Breath of the Wild is a game that basically defined the Switch generation and Tears of the Kingdom is a stellar send-off/love letter to the possibilities that the Switch brings to the table.\u003c/p\u003e\n\u003cp\u003eI'd really loved to have been able to write about Tears of the Kingdom without comparing it to Breath of the Wild, but after three failed drafts I've accepted that this is impossible because the two games complement eachother so much. If you got a kid a Switch and only Breath of the Wild/Tears of the Kingdom, they'd be set for a long time. At the very least I've never written about Breath of the Wild on this blog, so I guess this is okay.\u003c/p\u003e\n\u003ch2\u003eBreath of the Wild\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"https://cdn.xeiaso.net/file/christine-static/blog/2023/totk/botw-perch.jpg\" target=\"_blank\"\u003e\u003cpicture class=\"picture\" style=\"margin:0\"\u003e\u003csource type=\"image/avif\" srcset=\"https://cdn.xeiaso.net/file/christine-static/blog/2023/totk/botw-perch.avif\"\u003e\u003csource type=\"image/webp\" srcset=\"https://cdn.xeiaso.net/file/christine-static/blog/2023/totk/botw-perch.webp\"\u003e\u003cimg class=\"picture\" style=\"padding:0\" loading=\"lazy\" alt=\"hero image blog/2023/totk/botw-perch\" src=\"https://cdn.xeiaso.net/file/christine-static/blog/2023/totk/botw-perch-smol.png\"\u003e\u003c/picture\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eBreath of the Wild is a nearly flawless game. It's made its mark in open-world game design and you can clearly see a clear delineation between games that came out before and after it. It's aging like a fine wine and I can only see it getting better as time goes on.\u003c/p\u003e\n\u003cp\u003eOne of the biggest innovations that it brings to the genre is that they tried to solve the problem you get when you've already saved the world. Normally when you finish an open-world game, all of the map items are checked off. All of the enemies are no more. All of the chests are opened and empty. Everything is at peace. It gets really boring from here.\u003c/p\u003e\n\u003cp\u003eBreath of the Wild solved this by adding the \u003ca href=\"https://zelda.fandom.com/wiki/Blood_Moon\"\u003eBlood Moon\u003c/a\u003e system to respawn all enemies, overworld chests, and other internal variables. This happens about once every three hours of gameplay and normally happens at midnight. However, there's also a second type of blood moon called a \u0026quot;panic blood moon\u0026quot; that can occur whenever the game runs out of certain pools of memory. This means that programming errors and other things can cause a blood moon to occur at any time, \u003ca href=\"https://youtu.be/i9bkKw32dGw\"\u003eSonic 3D Blast style\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eThis is in stark contrast to things like The Elder Scrolls 5: Skyrim. In Skyrim your in-game quest list never gets empty because after a certain point people in the villages will just start randomly generating quests for you. This means that you can't truly get 100% quest completion, to the frustration of many completionists.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eBreath of the Wild mitigates a lot of this by giving the player radical mechanical freedom.\n\u003cul\u003e\n\u003cli\u003eIf you see something, you can probably climb it\u003c/li\u003e\n\u003cli\u003eIf you can climb it, you can glide out from it\u003c/li\u003e\n\u003cli\u003eAbove all, the game seems to be designed to keep you slightly out of your comfort zone.\n\u003cul\u003e\n\u003cli\u003eYour weapons aren't very durable, so you're constantly having to find new ones and adapt your strategy\u003c/li\u003e\n\u003cli\u003eYour shields can break if you mis-step too many times, so it subtly encourages you to take advantage of all of the game systems you can\u003c/li\u003e\n\u003cli\u003eYou can't just run around and kill everything, because you'll run out of weapons\u003c/li\u003e\n\u003cli\u003eYou need to toe the line between being careless and over-prepared\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003eThe game's story is very minimal\n\u003cul\u003e\n\u003cli\u003eYou wake up in the shrine of resurrection feeling like P. Diddy, grab your tablet, get out the door, and start to hit the ground running\u003c/li\u003e\n\u003cli\u003eYour goal: kill ganon\u003c/li\u003e\n\u003cli\u003eYour tools: whatever you can find\u003c/li\u003e\n\u003cli\u003eYour reward: saving the princess\u003c/li\u003e\n\u003cli\u003eLink is a nonverbal nonbinary twink so that you can project yourself into his shoes\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003eThe game gives you a few tools to help you on your way\n\u003cul\u003e\n\u003cli\u003eStasis to freeze and fling things\u003c/li\u003e\n\u003cli\u003eBombs to blow things up\u003c/li\u003e\n\u003cli\u003eMagnesis to move metal objects around\u003c/li\u003e\n\u003cli\u003eCryonis to make platforms in water\u003c/li\u003e\n\u003cli\u003eAnd finally a paraglider to soar around\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003eYou have so much freedom to explore\u003c/li\u003e\n\u003cli\u003eBreath of the Wild has flaws\n\u003cul\u003e\n\u003cli\u003eThe difficulty in normal mode is a bit too easy and master mode is a bit too hard\n\u003cul\u003e\n\u003cli\u003eThe early game armor is a bit too cheap and easy to mine on your way to Kakariko village, making you relatively safe early on\u003c/li\u003e\n\u003cli\u003eOnce you run to Faron and grab hearty durians, any attempt the game has to balance your health is broken\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003eThe durability system is a divisive feature\n\u003cul\u003e\n\u003cli\u003eI think it's ultimately a good thing, but a lot of people I know don't like it enough that they mod the game to greatly increase durability caps on each weapon.\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003eRevali's Gale basically makes all your movement tech obsolete\u003c/li\u003e\n\u003cli\u003eBombs are a bit too good\n\u003cul\u003e\n\u003cli\u003eInfinite sources of 20 damage is a bit too conveninent in a game that is supposed to make you feel slightly uncomfortable\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003eIt's really hard to find these flaws though\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2\u003eTears of the Kingdom\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"https://cdn.xeiaso.net/file/christine-static/blog/2023/totk/totk-vibes.jpg\" target=\"_blank\"\u003e\u003cpicture class=\"picture\" style=\"margin:0\"\u003e\u003csource type=\"image/avif\" srcset=\"https://cdn.xeiaso.net/file/christine-static/blog/2023/totk/totk-vibes.avif\"\u003e\u003csource type=\"image/webp\" srcset=\"https://cdn.xeiaso.net/file/christine-static/blog/2023/totk/totk-vibes.webp\"\u003e\u003cimg class=\"picture\" style=\"padding:0\" loading=\"lazy\" alt=\"hero image blog/2023/totk/totk-vibes\" src=\"https://cdn.xeiaso.net/file/christine-static/blog/2023/totk/totk-vibes-smol.png\"\u003e\u003c/picture\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eTears of the kingdom is a generational leap forward\n\u003cul\u003e\n\u003cli\u003eEverything has been rebalanced\n\u003cul\u003e\n\u003cli\u003eThe game's difficulty is somewhere between normal and master mode in breath of the wild\u003c/li\u003e\n\u003cli\u003eEnemies in general have more health, but you have more tools to deal with them\u003c/li\u003e\n\u003cli\u003eFuse lets you solve the durability system because every time you fuse a weapon you add a significant amount of durability to it\u003c/li\u003e\n\u003cli\u003eArmor is very expensive early on\u003c/li\u003e\n\u003cli\u003eHearty durians were completely removed\u003c/li\u003e\n\u003cli\u003eRevali's Gale was removed\n\u003cul\u003e\n\u003cli\u003eTulin's sage power lets you go horizontally instead\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003eBombs are now a limited use item\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003eYour powers are different yet fair\n\u003cul\u003e\n\u003cli\u003eMagnesis got a huge buff and now you can build your own vehicles, bridges, and more\u003c/li\u003e\n\u003cli\u003eYou can fuse monster parts to your weapons and shields\n\u003cul\u003e\n\u003cli\u003eA rock on the end of a stick can be a hammer for mining\u003c/li\u003e\n\u003cli\u003eA bomb on a shield can let you fly around\u003c/li\u003e\n\u003cli\u003eLizalfos and Bokoblin horns can let you get early game decent weapons without too much trouble\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003eAscend lets you leave places when things aren't going your way\u003c/li\u003e\n\u003cli\u003eRecall lets you turn back the clock on your mistakes or your enemies throwing things at you\u003c/li\u003e\n\u003cli\u003eAutobuild lets you remake builds you like\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003eIt's honestly such a huge improvement in every aspect\n\u003cul\u003e\n\u003cli\u003ePerformance is better thanks to the use of FSR for upscaling\u003c/li\u003e\n\u003cli\u003eThe game physics engine seems to have fixed some long-standing problems with game physics\n\u003cul\u003e\n\u003cli\u003eThe whole \u0026quot;jump onto a moving platform\u0026quot; thing is nearly impossible to do in about any other game engine\u003c/li\u003e\n\u003cli\u003eThe fact that player builds function at all without becoming jiggly messes is amazing\n\u003cul\u003e\n\u003cli\u003eThough for a while speedrunners were gluing an apple to a plank of wood to speed around the map\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003eThe game's story is a bit more fleshed out\n\u003cul\u003e\n\u003cli\u003eI don't really think this is a huge plus, but it's nice to have\u003c/li\u003e\n\u003cli\u003eThe game's story is still very minimal, but it's a bit more than \u0026quot;kill ganon\u0026quot;\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003eThe world has evolved since you last ventured out\n\u003cul\u003e\n\u003cli\u003eIt's about 7 years in the future since the events of Breath of the Wild\u003c/li\u003e\n\u003cli\u003eBreath of the Wild came out 7 years ago\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003eTears of the Kingdom is the standard that open-world games are going to be compared to. It is a fantastic achievement\n\u003cul\u003e\n\u003cli\u003eIt is a $70 game, but it comes with easily a hundred hours of enjoyment\u003c/li\u003e\n\u003cli\u003eIt is a fully featured game that you can play offline without DRM\u003c/li\u003e\n\u003cli\u003eYou can play it on your TV or on the go\u003c/li\u003e\n\u003cli\u003eIt's easy to tackle parts of the game in easily digestible chunks\u003c/li\u003e\n\u003cli\u003eThere's no DLC or microtransactions gating essential game features\u003c/li\u003e\n\u003cli\u003eIt's a game that you can play for a long time and still find new things to do\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003eTears of the Kingdom is a 10/10 game.\u003c/li\u003e\n\u003c/ul\u003e",
+ "date": "2023-09-11",
+ "mentions": null,
+ "readTimeEstimateMinutes": 541000000000
+ },
+ {
+ "frontMatter": {
+ "title": "Reaching the Unix Philosophy's Logical Extreme with Webassembly",
+ "date": "2023-08-27",
+ "slides_link": "https://drive.google.com/file/d/1j7sTnS2bs2XdvbpF2-1sncfkcPl8jRpE/view?usp=sharing"
+ },
+ "link": "talks/unix-philosophy-logical-extreme-wasm",
+ "bodyHTML": "\u003cbr /\u003e\n\u003cp\u003e\u003cdiv id=\"5b1484f2520949d9b43e92ba09dd9d52\"\u003e\u003cnoscript\u003e\u003cdiv class=\"warning\"\u003e\u003cdiv class=\"conversation\"\u003e\u003cdiv class=\"conversation-standalone\"\u003e\u003cpicture\u003e\u003csource type=\"image/avif\" srcset=\"https://cdn.xeiaso.net/file/christine-static/stickers/aoi/coffee.avif\"\u003e\u003csource type=\"image/webp\" srcset=\"https://cdn.xeiaso.net/file/christine-static/stickers/aoi/coffee.webp\"\u003e\u003cimg style=\"max-height:4.5rem\" alt=\"Aoi is coffee\" loading=\"lazy\" src=\"https://cdn.xeiaso.net/file/christine-static/stickers/aoi/coffee.png\"\u003e\u003c/picture\u003e\u003c/div\u003e\u003cdiv class=\"conversation-chat\"\u003e\u0026lt;\u003ca href=\"/characters#aoi\"\u003e\u003cb\u003eAoi\u003c/b\u003e\u003c/a\u003e\u0026gt; This dynamic component requires JavaScript to function, sorry!\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\u003c/noscript\u003e\u003c/div\u003e\n\u003cscript type=\"module\"\u003e\nimport Component from \"/static/xeact/Video.js?cacheBuster=5b1484f2520949d9b43e92ba09dd9d52\";\n\nconst g = (name) =\u003e document.getElementById(name);\nconst x = (elem) =\u003e {\n while (elem.lastChild) {\n elem.removeChild(elem.lastChild);\n }\n};\n\nconst root = g(\"5b1484f2520949d9b43e92ba09dd9d52\");\nx(g);\n\nroot.appendChild(Component({\"path\":\"talks/2023/gceu-wasm/video\"}))\n\u003c/script\u003e\n\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://youtu.be/QNDvfez6QL0\"\u003eYouTube link\u003c/a\u003e (please let me know if\nthe iframe doesn't work for you)\u003c/p\u003e\n\u003cp\u003e\u003cdiv class=\"hero xeblog-slides-essential\"\u003e\u003cpicture style=\"margin:0\"\u003e\u003csource type=\"image/avif\" srcset=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/001.avif\"\u003e\u003csource type=\"image/webp\" srcset=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/001.webp\"\u003e\u003cimg style=\"padding:0\" loading=\"lazy\" src=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/001-smol.png\"\u003e\u003c/picture\u003e\u003c/div\u003e\u003c/p\u003e\n\u003cp\u003eGood morning Berlin! How're you doing this fine morning? I'm Xe and today I'm gonna talk about something that I'm really excited about:\u003c/p\u003e\n\u003cp\u003eWebAssembly. WebAssembly is a compiler target for an imaginary CPU that your phones, tablets, laptops, gaming towers and even watches can run. It's intended to be a level below JavaScript to allow us to ship code in maintainable languages. Today I'm gonna be talking about fun ways you can take advantage of WebAssembly, but first we need to talk about the other main part of this subject:\u003c/p\u003e\n\u003cp\u003eUnix. Unix is the sole survivor of the early OS wars. It's not really that exciting from a computer science standpoint other than it was where C became popular and it uses file and filesystem API calls to interface with a lot of hardware.\u003c/p\u003e\n\u003cp\u003eDealing with some source code files? Discover them in the filesystem in your home directory and write to them with the file API.\u003c/p\u003e\n\u003cp\u003eDealing with disks? Discover them in the filesystem and manage them with the file API.\u003c/p\u003e\n\u003cp\u003eDesign is rooted in philosophy, and Unix has a core philosophy that all the decisions stem from. This is usually quoted as \u0026quot;everything is a file\u0026quot; but what does that even mean? How does that handle things that aren't literally files?\u003c/p\u003e\n\u003cp\u003e(Pause)\u003c/p\u003e\n\u003cp\u003eAnd wait, who's this Xe person?\u003c/p\u003e\n\u003cp\u003e\u003cdiv class=\"hero xeblog-slides-essential\"\u003e\u003cpicture style=\"margin:0\"\u003e\u003csource type=\"image/avif\" srcset=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/007.avif\"\u003e\u003csource type=\"image/webp\" srcset=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/007.webp\"\u003e\u003cimg style=\"padding:0\" loading=\"lazy\" src=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/007-smol.png\"\u003e\u003c/picture\u003e\u003c/div\u003e\u003c/p\u003e\n\u003cp\u003eLike the nice person with that microphone said, I'm Xe. I'm the person that put IPv6 packets into S3 and I work at Tailscale doing developer relations. I'm also the only person I know with the job title of Archmage. I'm a prolific blogger and I live in Ottawa in Canada with my husband.\u003c/p\u003e\n\u003cp\u003eI'm also a philosopher. As a little hint for anyone here, when someone openly introduces themselves as a philosopher, you should know you're in for some fun.\u003c/p\u003e\n\u003cp\u003e\u003cdiv class=\"hero xeblog-slides-essential\"\u003e\u003cpicture style=\"margin:0\"\u003e\u003csource type=\"image/avif\" srcset=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/009.avif\"\u003e\u003csource type=\"image/webp\" srcset=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/009.webp\"\u003e\u003cimg style=\"padding:0\" loading=\"lazy\" src=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/009-smol.png\"\u003e\u003c/picture\u003e\u003c/div\u003e\u003c/p\u003e\n\u003cp\u003eSpeaking of fun, I know you got up early for this talk because it sold itself as a WebAssembly talk, but I'm actually going to break a little secret with you. This isn't just a WebAssembly talk. This is an operating systems talk because most of the difficulties with using WebAssembly in the real world are actually operating systems issues. In this talk I'm going to start with the Unix philosophy, talk about how it relates to files, and then I'm gonna circle back to WebAssembly. Really, I promise.\u003c/p\u003e\n\u003cp\u003eSo, going back to where we were with Unix, what does it mean for everything to be a file? What is a file in the first place?\u003c/p\u003e\n\u003cp\u003eIn Unix, what we call \u0026quot;files\u0026quot; are actually just kernel objects we can make a bunch of calls to. And in a very Unix way, file handles aren't really opaque values; they are just arbitrary integers that just so happen to be indices into an array that lives in the struct your kernel uses to keep track of open files in that process. That is the main security model for access to files when running untrusted code in Linux processes.\u003c/p\u003e\n\u003cp\u003eSo with these array indices as arguments to some core system calls you can do some basic calls such as-\u003c/p\u003e\n\u003cp\u003e(Pause)\u003c/p\u003e\n\u003cp\u003eActually, now that I think about it, we just spent half an hour sitting and watching that lovely talk on the Go ecosystem. Let's do a little bit of exercise. Get that blood flowing!\u003c/p\u003e\n\u003cp\u003eSo how many of you can raise your hands? Keep them up, let's get those hands up!\u003c/p\u003e\n\u003cp\u003e(Pause)\u003c/p\u003e\n\u003cp\u003eAlright, alright, keep them up.\u003c/p\u003e\n\u003cp\u003e\u003cdiv class=\"hero xeblog-slides-essential\"\u003e\u003cpicture style=\"margin:0\"\u003e\u003csource type=\"image/avif\" srcset=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/016.avif\"\u003e\u003csource type=\"image/webp\" srcset=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/016.webp\"\u003e\u003cimg style=\"padding:0\" loading=\"lazy\" src=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/016-smol.png\"\u003e\u003c/picture\u003e\u003c/div\u003e\u003c/p\u003e\n\u003cp\u003eHow many of you have seen one of these 3d printed save icon things in person? If you have, keep your hand up. If not, put it down.\u003c/p\u003e\n\u003cp\u003e(Pause)\u003c/p\u003e\n\u003cp\u003e\u003cdiv class=\"hero xeblog-slides-essential\"\u003e\u003cpicture style=\"margin:0\"\u003e\u003csource type=\"image/avif\" srcset=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/017.avif\"\u003e\u003csource type=\"image/webp\" srcset=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/017.webp\"\u003e\u003cimg style=\"padding:0\" loading=\"lazy\" src=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/017-smol.png\"\u003e\u003c/picture\u003e\u003c/div\u003e\u003c/p\u003e\n\u003cp\u003eHow many of you have used one of them in school, at work, or even at home? If you have, keep it up, if not, put it down.\u003c/p\u003e\n\u003cp\u003e(Pause)\u003c/p\u003e\n\u003cp\u003eAlright, thanks again! One more time!\u003c/p\u003e\n\u003cp\u003e\u003cdiv class=\"hero xeblog-slides-essential\"\u003e\u003cpicture style=\"margin:0\"\u003e\u003csource type=\"image/avif\" srcset=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/018.avif\"\u003e\u003csource type=\"image/webp\" srcset=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/018.webp\"\u003e\u003cimg style=\"padding:0\" loading=\"lazy\" src=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/018-smol.png\"\u003e\u003c/picture\u003e\u003c/div\u003e\u003c/p\u003e\n\u003cp\u003eHow about one of these audio-only VHS tapes? Keep it up or put it down.\u003c/p\u003e\n\u003cp\u003eAlright, for those of you with your hands up, it's probably time to schedule that colonoscopy. Take advantage of that socialized medicine! You can put your hands down now, I don't want to be liable.\u003c/p\u003e\n\u003cp\u003e(Audience laughs)\u003c/p\u003e\n\u003cp\u003eFor the gen-zed in the crowd that had no idea what these things are, a cassette tape was what we used to store music on back when there were 9 planets.\u003c/p\u003e\n\u003cp\u003e(Audience laughs)\u003c/p\u003e\n\u003cp\u003eSo when I say files, let's think about these. Cassette tapes. Cassette tapes have the same basic usage properties as files.\u003c/p\u003e\n\u003cp\u003eTo start, you can read from files and play music from a cassette tape in all that warm analog goodness. You can write to files and record audio to a cassette tape. Know the term \u0026quot;mixtape\u0026quot;? That's where it comes from. You can also open files and insert a cassette tape into a tape player. When you're done with them, you can close files and remove tapes from a tape player. And finally you can fast-forward and rewind the tape to find the song you want. Imagine that Gen Z, imagine having to find your songs on the tape instead of skipping right to them.\u003c/p\u003e\n\u003cp\u003eAnd these same calls work on log files, hard drives, and more. These 5 basic calls are the heart of Unix that everything spills out from, and this basic model gets you so far that it's how this little OS you've never heard of called Plan 9 works.\u003c/p\u003e\n\u003cp\u003eBut what about things that don't directly map to files? What about network sockets? Network sockets are the abstraction that Unix uses to let applications connect to another computer over a network like the internet. You can open sockets, you can close them, you can read from them, you can write to them. But are they files?\u003c/p\u003e\n\u003cp\u003e\u003cdiv class=\"hero xeblog-slides-essential\"\u003e\u003cpicture style=\"margin:0\"\u003e\u003csource type=\"image/avif\" srcset=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/026.avif\"\u003e\u003csource type=\"image/webp\" srcset=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/026.webp\"\u003e\u003cimg style=\"padding:0\" loading=\"lazy\" src=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/026-smol.png\"\u003e\u003c/picture\u003e\u003c/div\u003e\u003c/p\u003e\n\u003cp\u003eTurns out, they are! In Unix you use mostly the same calls for dealing with network sockets that you do for files. Network sockets are treated like one of these things: an AUX cable to cassette tape adaptor. This was what we used to use in order to get our MP3 players, CD players, Gameboys, and smartphones connected up to the car stereo. This isn't a bit, we actually used these a lot. Yes, we actually used these. I used one extensively when I was delivering pizzas in high school to get the turn by turn navigation directions read out loud to me. We had no other options before Bluetooth existed. It was our only compromise.\u003c/p\u003e\n\u003cp\u003e(Audience laughs)\u003c/p\u003e\n\u003cp\u003eHow about processes? Those are known to be another hallmark of the Unix philosophy. The Unix philosophy is also understood to be that programs should be filters that take the input and spruce it up for the next stage of the pipeline. Under the hood, are those files?\u003c/p\u003e\n\u003cp\u003e\u003cdiv class=\"hero xeblog-slides-essential\"\u003e\u003cpicture style=\"margin:0\"\u003e\u003csource type=\"image/avif\" srcset=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/028.avif\"\u003e\u003csource type=\"image/webp\" srcset=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/028.webp\"\u003e\u003cimg style=\"padding:0\" loading=\"lazy\" src=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/028-smol.png\"\u003e\u003c/picture\u003e\u003c/div\u003e\u003c/p\u003e\n\u003cp\u003eYep! Turns out they're three files: input from the last program in the chain, output to the next program in the chain, and error messages to either a log file or operator. All those pipelines in your shell script abominations that you are afraid to touch (and somehow load-bearing for all of production for several companies) become data passing through those three basic files.\u003c/p\u003e\n\u003cp\u003e\u003cdiv class=\"hero xeblog-slides-essential\"\u003e\u003cpicture style=\"margin:0\"\u003e\u003csource type=\"image/avif\" srcset=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/029.avif\"\u003e\u003csource type=\"image/webp\" srcset=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/029.webp\"\u003e\u003cimg style=\"padding:0\" loading=\"lazy\" src=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/029-smol.png\"\u003e\u003c/picture\u003e\u003c/div\u003e\u003c/p\u003e\n\u003cp\u003eIt's like an assembly line for your data, every step gets its data fed from the output of the last one and then it sends its results to the input of the next one. Errors might to go an operator or a log sink like the journal, but it goes down the chain and lets you do whatever you want. Really, it's a rather elegant design, there's a reason it's lasted for over 50 years.\u003c/p\u003e\n\u003cp\u003e\u003cdiv class=\"hero xeblog-slides-essential\"\u003e\u003cpicture style=\"margin:0\"\u003e\u003csource type=\"image/avif\" srcset=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/030.avif\"\u003e\u003csource type=\"image/webp\" srcset=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/030.webp\"\u003e\u003cimg style=\"padding:0\" loading=\"lazy\" src=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/030-smol.png\"\u003e\u003c/picture\u003e\u003c/div\u003e\u003c/p\u003e\n\u003cp\u003eSo you know how I promised that I'm gonna relate all this back to WebAssembly? Here's when. Now that we understand what Unix is, let's talk about what WebAssembly by itself isn't.\u003c/p\u003e\n\u003cp\u003eWebAssembly is a CPU that can run pure functions and then return the results. It can also poke the outside world in a limited capacity, but overall it's a lot more like this in practice:\u003c/p\u003e\n\u003cp\u003e\u003cdiv class=\"hero xeblog-slides-essential\"\u003e\u003cpicture style=\"margin:0\"\u003e\u003csource type=\"image/avif\" srcset=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/032.avif\"\u003e\u003csource type=\"image/webp\" srcset=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/032.webp\"\u003e\u003cimg style=\"padding:0\" loading=\"lazy\" src=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/032-smol.png\"\u003e\u003c/picture\u003e\u003c/div\u003e\u003c/p\u003e\n\u003cp\u003eA microcontroller. Sure you can use microcontrollers to do a lot of useful things (especially when you throw in temperature sensors and maybe even a GSM shield to send text messages), but the main thing that microcontrollers can't easily do is connect to the Internet or deal with files on storage devices. Pedantically, this is something you can do, but every time it'll need to be custom-built for the hardware in question. There's no operating system in the mix, so everything needs to have bespoke code. Without an operating system, there's no network stack or even processes. This makes it possible, but moderately difficult to reuse existing code from other projects. If you want to do something like run libraries you wrote in Go on the frontend, such as your peer to peer VPN engine and all of its supporting code, you'd need to either do a pile of ridiculous hacks or you'd just need there to be something close to an operating system. Those hacks would be fairly reliable, but I think we can do better.\u003c/p\u003e\n\u003cp\u003e(Pause)\u003c/p\u003e\n\u003cp\u003eTurns out, you don't need an operating system to fill most of the gaps that are left when you don't have one. In WebAssembly, we have something to fill this gap:\u003c/p\u003e\n\u003cp\u003eWASI. WASI is the WebAssembly System Interface. It defines some semantics for how input, output, files, filesystems, and basic network sockets should be implemented for both the guest program and the host environment. This acts like enough of an \u0026quot;operating system\u0026quot; as far as programming languages care. When you get the Go or Rust compiler to target WASI, they'll emit binaries that can run just about anywhere with a WASI runtime.\u003c/p\u003e\n\u003cp\u003e\n\u003cdiv class=\"conversation\"\u003e\n \u003cdiv class=\"conversation-standalone\"\u003e\n \u003cimg src=\"https://cdn.xeiaso.net/sticker/cadey/enby/128\" alt=\"Cadey is enby\"\u003e\n \u003c/div\u003e\n \u003cdiv class=\"conversation-chat\"\u003e\u0026lt;\u003ca href=\"/characters#cadey\"\u003e\u003cb\u003eCadey\u003c/b\u003e\u003c/a\u003e\u0026gt; I wonder which thing that runs on several billion devices, including the SIM cards in your phones and an alarming number of life-critical devices this reminds you of.\u003c/div\u003e\u003c/div\u003e\u003c/p\u003e\n\u003cp\u003eSo circling back on the filesystem angle, one of the key distinctions with how WASI implements filesystem access compared to other operating systems is that there's no expectation for running processes to have access to the host filesystem, or even any filesystem at all. It is perfectly legal for a WASI module to run without filesystem access. More critically for the point I'm trying to build up to though, there are a few files that are guaranteed to exist:\u003c/p\u003e\n\u003cp\u003e\u003cdiv class=\"hero xeblog-slides-essential\"\u003e\u003cpicture style=\"margin:0\"\u003e\u003csource type=\"image/avif\" srcset=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/028.avif\"\u003e\u003csource type=\"image/webp\" srcset=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/028.webp\"\u003e\u003cimg style=\"padding:0\" loading=\"lazy\" src=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/028-smol.png\"\u003e\u003c/picture\u003e\u003c/div\u003e\u003c/p\u003e\n\u003cp\u003eStandard input, output, and error files. And, you know what this means? This means we can circle back to the Unix idea of WebAssembly programs being filters. You can make a WebAssembly program take input and emit output as one step in a longer process. Just like your pipelines!\u003c/p\u003e\n\u003cp\u003eAs an example, I have an overcomplicated blog engine that includes its own dialect of markdown because of course it does. After getting nerd sniped by Amos, I rewrote it all in Rust; but when I did that, I separated the markdown parser into its own library and made a little command-line utility for it. I compiled that to WebAssembly with WASI and now I think I'm one of the only people to have successfully distributed a program over the fediverse: the library that I use to convert markdown to HTML, with the furry avatar templates that orange websites hate and all.\u003c/p\u003e\n\u003cp\u003e\u003cdiv class=\"hero xeblog-slides-essential\"\u003e\u003cpicture style=\"margin:0\"\u003e\u003csource type=\"image/avif\" srcset=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/039.avif\"\u003e\u003csource type=\"image/webp\" srcset=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/039.webp\"\u003e\u003cimg style=\"padding:0\" loading=\"lazy\" src=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/039-smol.png\"\u003e\u003c/picture\u003e\u003c/div\u003e\u003c/p\u003e\n\u003cp\u003eJust to help hammer this all in, I'm going to show you some code I wrote between episodes of anime and donghua. I wrote a little \u0026quot;echo server\u0026quot; that takes a line of input, runs a WebAssembly program on that line of input fed into standard in, and then returns the response from standard out. The first program I'm gonna show off is going to be a \u0026quot;reply with the input\u0026quot; program. Then, I'm going to switch it over to my markdown library I mentioned and write out a message to get turned into HTML. I'm going to connect to it with another WebAssembly program that has a custom filesystem configuration that lets you use the network as a filesystem because WASI's preview 1 API doesn't support making outgoing network connections at the time of writing. If sockets really are just files, then why can't we just use the network stack as a filesystem?\u003c/p\u003e\n\u003cp\u003eNow, it's time, let's show off the power of WebAssembly. But first, the adequate prayers are needed: Demo gods, hear my cries. Bless my demo!\u003c/p\u003e\n\u003cp\u003e\n\u003cdiv class=\"conversation\"\u003e\n \u003cdiv class=\"conversation-standalone\"\u003e\n \u003cimg src=\"https://cdn.xeiaso.net/sticker/mara/hacker/128\" alt=\"Mara is hacker\"\u003e\n \u003c/div\u003e\n \u003cdiv class=\"conversation-chat\"\u003e\u0026lt;\u003ca href=\"/characters#mara\"\u003e\u003cb\u003eMara\u003c/b\u003e\u003c/a\u003e\u0026gt; For obvious reasons it's difficult to put the demo into the transcript, but you can find the code for it \u003ca href=\"https://github.com/Xe/x/tree/master/conferences/gceu23\"\u003ein github.com/Xe/x\u003c/a\u003e.\u003c/div\u003e\u003c/div\u003e\u003c/p\u003e\n\u003cp\u003eOn the right hand side I have a terminal running that WebAssembly powered echo server I mentioned. Just to prove I didn't prerecord this, someone yell out something for me to type into the WebAssembly program on the left.\u003c/p\u003e\n\u003cp\u003e(Pause for someone to shout something)\u003c/p\u003e\n\u003cp\u003eCool, let's type it in:\u003c/p\u003e\n\u003cp\u003e(Type it in and hit enter)\u003c/p\u003e\n\u003cp\u003eSee? I didn't prerecord this and that lovely member of the audience wasn't a plant to make this easier on me.\u003c/p\u003e\n\u003cp\u003e(Audience laughs)\u003c/p\u003e\n\u003cp\u003eYou know what, while we're at it, let's do a little bit more. I have another version of this set up where it feeds things into that markdown-\u0026gt;HTML parser I mentioned. If I write some HTML into there:\u003c/p\u003e\n\u003cp\u003e(Type it in and hit enter)\u003c/p\u003e\n\u003cp\u003eAs you can see, I get the template expanded and all of the HTML goodness has come back to haunt us again. Even though the program on the right is written in Go:\u003c/p\u003e\n\u003cp\u003e(I press control-backslash to cause the go runtime to vomit the stack of every goroutine, attempting to prove that there's nothing up my sleeve)\u003c/p\u003e\n\u003cp\u003eIt's able to run that Rust program like it's nothing.\u003c/p\u003e\n\u003cp\u003e(Applause)\u003c/p\u003e\n\u003cp\u003eThank Klaus that all that worked. I'm going to put all the code for this on my GitHub page in my \u003ca href=\"https://github.com/Xe/x\"\u003ex repo\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eThis technique of embedding Rust programs into Go programs is something I call crabtoespionage. It lets you use the filter property of Unix programs as functions in your Go code. This is how you Rustaceans in crowd can sneak some Rust into prod without having to make sacrifices to the linker gods. I know there's at least one of you out there. Commit the .wasm bytes from rustc or cargo to your repo and then you can still build everything on a Mac, Plan 9, or even TempleOS, assuming you have Go running there.\u003c/p\u003e\n\u003cp\u003eMost of the heavy lifting in my examples is done with Wazero, it's a library for Go programs that is basically a WebAssembly VM and some hooks for WASI implemented for you. The flow for embedding Rust programs into Go looks like this:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eFirst, extract the subset of the library you want and make it a standalone program. This makes it easy to test things on the command line. Use arguments and command line flags, they're there for a reason.\u003c/li\u003e\n\u003cli\u003eNext, build that to WASI and fix things until it works. You'll have to figure out how to draw the rest of the owl here. Some things may be impossible depending on facts and circumstances. Usually things should work out.\u003c/li\u003e\n\u003cli\u003eThen import wazero into your program and set everything up by using the embed directive to hoist the WebAssembly bytes into your code. Set up the filesystems you want to use, and your runtime config and finally:\u003c/li\u003e\n\u003cli\u003eThen make a wrapper function that lets you go from input to output et voila!\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eYou've just snuck Rust into production. This is how I snuck Rust into prod at work and nobody is the wiser.\u003c/p\u003e\n\u003cp\u003e(Pause)\u003c/p\u003e\n\u003cp\u003eWait, I just gave it away, no, oops. Sorry! I had no choice. Mastodon HTML is weird. The Go HTML library is weirder.\u003c/p\u003e\n\u003cp\u003eThere's a few libraries on GitHub that use this basic technique for more than just piping input to output, they use it to embed C and C++ libraries into Go code. In the case of the regular expressions package, it can be faster than package regexp in some cases. Including the WebAssembly overhead. It's incredible. There's not even that many optimizations for WebAssembly yet!\u003c/p\u003e\n\u003cp\u003eNo C compiler required! No cross-compiling GCC required! No satanic sacrifices to the dark beings required! It's magic, just without the spell slots.\u003c/p\u003e\n\u003cp\u003eSo while we're on this, let's take both aspects of this to their logical conclusions. What about plugins for programs? There's plenty of reasons customers would want to have arbitrary plugin code running, and also plenty of reasons for you to fear having to run arbitrary customer plugin code. If we can run things in an isolated sandbox and then define our own filesystem logic: what if we expose application state as a filesystem? Trigger execution of the plugin code based on well-defined events that get piped to standard input. Make open calls fetch values from an API or write new values to that same API.\u003c/p\u003e\n\u003cp\u003eThis is how the ACME editor for Plan 9 works. It exposes internal application state as a filesystem for plugins to manipulate to their pleasure.\u003c/p\u003e\n\u003cp\u003eSo, to wrap all of this up:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eWhen you're dealing with Unix, you're dealing with files, be they source code, hard drives, or network sockets.\u003c/li\u003e\n\u003cli\u003eLike anything made around a standards body, even files themselves are lies and anything can be a file if it lies enough in the right way.\u003c/li\u003e\n\u003cli\u003eUnderstanding that everything is founded on these lies frees you from the expectation of trying to stay consistent with them. This lets you run things wherever without having to have a C compiler toolchain for win32 handy.\u003c/li\u003e\n\u003cli\u003eBecause everything is based on these lies, if you control what lies are being used, you actually end up controlling the truths that users deal with. When you free yourself from the idea of having to stay consistent with previous interpretation of those lies, you are free to do whatever you want.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eHow could you use this in your projects to do fantastic new things? The ball's in your court Creators.\u003c/p\u003e\n\u003cp\u003e(Applause)\u003c/p\u003e\n\u003cp\u003e\u003cdiv class=\"hero xeblog-slides-essential\"\u003e\u003cpicture style=\"margin:0\"\u003e\u003csource type=\"image/avif\" srcset=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/057.avif\"\u003e\u003csource type=\"image/webp\" srcset=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/057.webp\"\u003e\u003cimg style=\"padding:0\" loading=\"lazy\" src=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/057-smol.png\"\u003e\u003c/picture\u003e\u003c/div\u003e\u003c/p\u003e\n\u003cp\u003eWith all that said, here's a giant list of everyone that's helped me with this talk, the research I put into the talk, and uncountable other things. Thank you so much everyone.\u003c/p\u003e\n\u003cp\u003e(Thunderous applause)\u003c/p\u003e\n\u003cp\u003e\u003cdiv class=\"hero xeblog-slides-essential\"\u003e\u003cpicture style=\"margin:0\"\u003e\u003csource type=\"image/avif\" srcset=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/058.avif\"\u003e\u003csource type=\"image/webp\" srcset=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/058.webp\"\u003e\u003cimg style=\"padding:0\" loading=\"lazy\" src=\"https://cdn.xeiaso.net/file/christine-static/talks/2023/gceu-wasm/058-smol.png\"\u003e\u003c/picture\u003e\u003c/div\u003e\u003c/p\u003e\n\u003cp\u003eAnd with that, I've been Xe! Thank you so much for coming out to Berlin. I'll be wandering around if you have any questions for me, but if I miss it, please do email me at \u003ca href=\"mailto:crabtoespionage@xeserv.us\"\u003ecrabtoespionage@xeserv.us\u003c/a\u003e. I'll reply to your questions, really. My example code is in the conferences folder of my experimental repo \u003ca href=\"https://github.com/Xe/x\"\u003egithub.com/Xe/x\u003c/a\u003e. Otherwise, please make sure to stay hydrated and enjoy the conference! Be well!\u003c/p\u003e",
+ "date": "2023-08-27",
+ "mentions": null,
+ "readTimeEstimateMinutes": 980000000000
+ },
+ {
+ "frontMatter": {
+ "title": "How to use Tailwind CSS in your Go programs",
+ "date": "2023-08-27",
+ "series": "howto",
+ "tags": [
+ "go",
+ "tailwind",
+ "css"
+ ]
+ },
+ "link": "blog/using-tailwind-go",
+ "bodyHTML": "\u003cp\u003e\u003cmeta property=\"og:image\" content=\"https://cdn.xeiaso.net/file/christine-static/hero/grass-smol.png\"\u003e\u003cfigure class=\"hero\" style=\"margin:0\"\u003e\u003cpicture style=\"margin:0\"\u003e\u003csource type=\"image/avif\" srcset=\"https://cdn.xeiaso.net/file/christine-static/hero/grass.avif\"\u003e\u003csource type=\"image/webp\" srcset=\"https://cdn.xeiaso.net/file/christine-static/hero/grass.webp\"\u003e\u003cimg style=\"padding:0\" loading=\"lazy\" alt=\"hero image grass\" src=\"https://cdn.xeiaso.net/file/christine-static/hero/grass-smol.png\"\u003e\u003c/picture\u003e\u003cfigcaption\u003eNikon D3300, Holga Lens, Photo by Xe Iaso -- A dithered picture of grass, vignetted around the edges. The palette is forced to be very natural.\u003c/figcaption\u003e\u003c/figure\u003e\u003c/p\u003e\n\u003cp\u003eWhen I work on some of my smaller projects, I end up hitting a point where I need more than \u003ca href=\"https://github.com/Xe/Xess\"\u003eminimal CSS\u003c/a\u003e configuration. I don't want to totally change my development flow to bring in a bunch of complicated toolkits or totally rewrite my frontend in React or something, I just want to make things not look like garbage. Working with CSS by itself can be annoying.\u003c/p\u003e\n\u003cp\u003e\n\u003cdiv class=\"conversation\"\u003e\n \u003cdiv class=\"conversation-smol\"\u003e\n \u003cimg src=\"https://cdn.xeiaso.net/sticker/aoi/coffee/64\" alt=\"Aoi is coffee\"\u003e\n \u003c/div\u003e\n \u003cdiv class=\"conversation-chat\"\u003e\u0026lt;\u003ca href=\"/characters#aoi\"\u003e\u003cb\u003eAoi\u003c/b\u003e\u003c/a\u003e\u0026gt; Heck yes it is! I don't like working with CSS because it never really feels like I'm making progress. I'm always fighting with it. It's just left me with the impression that I'm just fundamentally \u003cem\u003ebad\u003c/em\u003e at frontend work.\u003c/div\u003e\u003c/div\u003e\n\n\u003cdiv class=\"conversation\"\u003e\n \u003cdiv class=\"conversation-smol\"\u003e\n \u003cimg src=\"https://cdn.xeiaso.net/sticker/numa/happy/64\" alt=\"Numa is happy\"\u003e\n \u003c/div\u003e\n \u003cdiv class=\"conversation-chat\"\u003e\u0026lt;\u003ca href=\"/characters#numa\"\u003e\u003cb\u003eNuma\u003c/b\u003e\u003c/a\u003e\u0026gt; TBH, you're not bad at frontend work. You're bad at design. The way you get better at design is by doing more attempts at it. You can't get better at design by avoiding it. I'm not saying you have to be a designer. I'm saying you have to be willing to try.\u003cbr /\u003e\u003cbr /\u003eRemember: ignorance is the default state.\u003c/div\u003e\u003c/div\u003e\n\n\u003cdiv class=\"conversation\"\u003e\n \u003cdiv class=\"conversation-smol\"\u003e\n \u003cimg src=\"https://cdn.xeiaso.net/sticker/aoi/wut/64\" alt=\"Aoi is wut\"\u003e\n \u003c/div\u003e\n \u003cdiv class=\"conversation-chat\"\u003e\u0026lt;\u003ca href=\"/characters#aoi\"\u003e\u003cb\u003eAoi\u003c/b\u003e\u003c/a\u003e\u0026gt; You know, I never really thought about it like that. I guess I'll give it a shot.\u003c/div\u003e\u003c/div\u003e\u003c/p\u003e\n\u003cp\u003eI've found a way to make working with CSS a lot easier for me. I've been starting to use \u003ca href=\"https://tailwindcss.com/\"\u003eTailwind\u003c/a\u003e in my personal and professional projects. Tailwind is a CSS framework that makes nearly \u003ca href=\"https://tailwindcss.com/docs/utility-first\"\u003eevery CSS behavior\u003c/a\u003e its own utility class. This means that you can make your HTML and CSS the same file, minimizing context switching between your HTML and CSS files. It also means that you can build your own components out of these utility classes. Here's an example of what it ends up looking like in practice:\u003c/p\u003e\n\u003cpre style=\"background-color:#3b3228;\"\u003e\u003ccode class=\"language-html\"\u003e\u003cspan style=\"color:#d0c8c6;\"\u003e\u0026lt;\u003c/span\u003e\u003cspan style=\"color:#cb6077;\"\u003ediv \u003c/span\u003e\u003cspan style=\"color:#d28b71;\"\u003eclass\u003c/span\u003e\u003cspan style=\"color:#d0c8c6;\"\u003e=\u0026quot;\u003c/span\u003e\u003cspan style=\"color:#beb55b;\"\u003ebg-blue-500 text-white font-bold py-2 px-4 rounded\u003c/span\u003e\u003cspan style=\"color:#d0c8c6;\"\u003e\u0026quot;\u0026gt;Button\u0026lt;/\u003c/span\u003e\u003cspan style=\"color:#cb6077;\"\u003ediv\u003c/span\u003e\u003cspan style=\"color:#d0c8c6;\"\u003e\u0026gt;\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eThis is a button that has a blue background, white text, is bold, has a padding of 2, and has a rounded border. This looks like a lot of CSS to write for a button, but it's all in one place and can be customized for every button. This is a lot easier to work with than having to context switch between your HTML and CSS files.\u003c/p\u003e\n\u003cp\u003e\n\u003cdiv class=\"conversation\"\u003e\n \u003cdiv class=\"conversation-smol\"\u003e\n \u003cimg src=\"https://cdn.xeiaso.net/sticker/aoi/wut/64\" alt=\"Aoi is wut\"\u003e\n \u003c/div\u003e\n \u003cdiv class=\"conversation-chat\"\u003e\u0026lt;\u003ca href=\"/characters#aoi\"\u003e\u003cb\u003eAoi\u003c/b\u003e\u003c/a\u003e\u0026gt; What's that unit in \u003ccode\u003epx-2\u003c/code\u003e, it's padding on the X axis by two what?\u003c/div\u003e\u003c/div\u003e\n\n\u003cdiv class=\"conversation\"\u003e\n \u003cdiv class=\"conversation-smol\"\u003e\n \u003cimg src=\"https://cdn.xeiaso.net/sticker/mara/happy/64\" alt=\"Mara is happy\"\u003e\n \u003c/div\u003e\n \u003cdiv class=\"conversation-chat\"\u003e\u0026lt;\u003ca href=\"/characters#mara\"\u003e\u003cb\u003eMara\u003c/b\u003e\u003c/a\u003e\u0026gt; It's \u003ca href=\"https://www.sitepoint.com/understanding-and-using-rem-units-in-css/\"\u003erem\u003c/a\u003e, which is about 16 pixels (assuming you don't change the font size). The exact size of rem units can be confusing at first, but you end up internalizing it over time. Think about it like this: \u003ccode\u003epr-1\u003c/code\u003e is about the size of a space between words.\u003c/div\u003e\u003c/div\u003e\u003c/p\u003e\n\u003cp\u003eOne of the biggest downsides is that Tailwind's compiler is written in JavaScript and distributed over \u003ca href=\"https://www.npmjs.com/\"\u003enpm\u003c/a\u003e. This is okay for people that are experienced JavaScript Touchers, but I am not one of them. Usually when I see that something requires me to use npm, I just close the tab and move on. Thankfully, Tailwind is actually a lot easier to use than