embedding things

A reference page, not a piece of writing: it exists so that every kind of embed is proved working rather than promised. Anything below can be copied straight into a new post in _posts/. Delete this file whenever it has stopped being useful — see the README.

Code, with syntax highlighting

Fenced blocks are highlighted server-side, so there is no flash of unstyled code.

// Interval arithmetic: every result carries a bound rather than a hopeful number.
struct Interval {
    double lo, hi;

    Interval operator*(const Interval& o) const {
        const double a = lo * o.lo, b = lo * o.hi;
        const double c = hi * o.lo, d = hi * o.hi;
        return {std::min({a, b, c, d}), std::max({a, b, c, d})};
    }

    bool definitely_positive() const { return lo > 0.0; }
};

Inline code works too, and so does maths — \(\varphi = \frac{1+\sqrt5}{2}\) — set with MathJax:

\[F_n = \frac{\varphi^n - (1-\varphi)^n}{\sqrt 5}\]

Images

Images go through figure.liquid, which handles responsive sizes, lazy loading and a WebP version for browsers that want one. Add zoomable=true and it opens on click.

A 2048 board, drawn to have something real to embed.

An embedded program

Anything that is a self-contained HTML page can be dropped into assets/ and framed in place — a compiled WebAssembly demo, a visualiser, a small game.

Interactive charts

Three chart libraries are wired in. Each is a fenced block of JSON — no JavaScript to write, no build step to run.

Plotly:

{
  "data": [
    {
      "x": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
      "y": [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89],
      "type": "scatter",
      "mode": "lines+markers",
      "name": "Fibonacci"
    }
  ],
  "layout": { "margin": { "t": 20 }, "yaxis": { "type": "log", "title": "value" }, "xaxis": { "title": "n" } }
}

Chart.js:

{
  "type": "bar",
  "data": {
    "labels": ["2", "4", "8", "16", "32", "64"],
    "datasets": [
      {
        "label": "illustrative tile counts",
        "data": [12, 9, 7, 5, 3, 1],
        "borderWidth": 1
      }
    ]
  },
  "options": { "scales": { "y": { "beginAtZero": true } } }
}

Vega-Lite:

{
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
  "description": "An illustrative scatter of search depth against time.",
  "data": {
    "values": [
      {"depth": 1, "ms": 0.02}, {"depth": 2, "ms": 0.05}, {"depth": 3, "ms": 0.14},
      {"depth": 4, "ms": 0.41}, {"depth": 5, "ms": 1.2}, {"depth": 6, "ms": 3.6}
    ]
  },
  "mark": {"type": "line", "point": true},
  "encoding": {
    "x": {"field": "depth", "type": "quantitative", "title": "search depth"},
    "y": {"field": "ms", "type": "quantitative", "scale": {"type": "log"}, "title": "milliseconds"}
  }
}

The numbers in the last two are placeholders, chosen to make the axes do something.

A Jupyter notebook

Notebooks are converted at build time and rendered inline, code and output together.

Internal links are ordinary paths: projects, apps, contact, and blog, and the PDF at /cv.pdf. The CV page is the one exception — it’s linked by its canonical address, cv, rather than a path on this host.