I Built This Website Using Racket. Here's What I Can Do Now.

Last updated:
Message of the Day

I designed this website with no ads or invasive tracking so that you find useful content quickly, easily, and with due respect for your privacy.

However, this makes me dependent on readers for funding. Ads and tracking are everywhere because most people do not pay for content and ignore messages like this one. Please support this website, and the products it brings you.

EDIT (March 22nd 2019): This website is now built using Unlike-Assets, the library on which Polyglot depends. This article is now an indirect demonstration of Polyglot's features.

It looks like there’s not much to see here. Isn’t it great? Barring exceptional cases, every page on this website takes up fewer than 20 kibibytes on disk and runs no JavaScript. Throttle your connection and hit refresh a few times. It loads in milliseconds. No one does that anymore.

It’s funny. I spent years learning the ins and outs of front-end web development, only to feel happy making a tiny, 90s-looking page that loads in a blink of an eye with nothing to do but make a point. Is that ironic? I haven’t decided yet.

But I did not just write vanilla HTML and give up my shiny toys. I use Racket to shape content using any language I want. What better tool for starting with a blank slate?

A Website With Superpowers

Racket is a programming language programming language. Racket allows you to write your own languages in terms of itself, and it is a joy to use. I released unlike-assets—an open source build tool that functions as an alternative to Webpack for my purposes—to say thank you and to show off what it can do.

I built a specific configuration for unlike-assets called polyglot that lets me write web content with arbitrary DSLs.

This snippet computes a Sierpinsky Triangle when building this page. I adapted the code below from goessner.net. A cool part about that is that I expressed the code only once. My system knows to display the code for you to read, and then run it.

#lang racket

(provide out)

(define (sierpinsky-triangle iterations color id-prefix)
  (define (gid n) (format "~a~e" id-prefix n))
  (define (rf n) (format "#~a" (gid n)))
  (define (iter n)
    `(g ((id ,(gid n)))
            (λ (matrix)
               `(use ((xlink:href ,(rf (- n 1))) (transform ,matrix))))
            '("matrix(0.5 0 0 0.5 0 0)"
              "matrix(0.5 0 0 0.5 1 0)"
              "matrix(0.5 0 0 0.5 0.5 0.866)"))))

  `(svg ((xmlns "http://www.w3.org/2000/svg")
         (xmlns:xlink "http://www.w3.org/1999/xlink")
         (style "display: block; margin: 0 auto")
         (width "200")
         (height "175"))
          (path ((id ,(gid 0)) (fill ,color) (d "M0 0,2 0,1 1.732 z"))))
              (range 1 (+ iterations 1)))
          (use ((xlink:href ,(rf iterations)) (transform "scale(100)")))))

(define out
  `(div ((style "margin: 0 auto"))
        ,(sierpinsky-triangle 4 "#800" "tri")))

Here I use rash—a shell dialect by William Hatch—to compute the download size of Bootstrap. Honestly, I did it just because I can. I’d have to configure Webpack for countless hours to get this kind of flexibility.

#lang rash

(provide out)
(define size-box (box "Unknown"))

curl -so /dev/null -w '"%{size_download}"' https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css |>> set-box! size-box

(define out
  `(span ((style "color: rebeccapurple"))
         "The total download size of Bootstrap v4.3.1 is "
         ,(unbox size-box) " bytes"))
The total download size of Bootstrap v4.3.1 is 155758 bytes

Here are some drum samples from rsound translated into an <audio> element without opening a sound editor on my PC.

This page starts as a Markdown file. Since Markdown supports use of HTML tags, I use <script> elements to express Racket modules that are available only within a page.

I parse the Markdown, process the Racket code in a sensible order, and then scan for dependencies in href and src attributes (such as other pages) to automagically build the rest of the website until all dependencies are fulfilled.

Things get fun when I can “drop down” from prose at any time to prepare a deeply integrated application that sits snug in any surrounding context. So if you are interested in using this kind of engine for your code, give polyglot a try. If you want to start from a lower level, then use unlike-assets.