title | author | keywords | |||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
An Introduction to Living Papers |
|
|
::: teaser {#teaser} | A teaser image for the beginning of our article. [Scroll down for an interactive version!]{.html:only} :::
::: abstract With Living Papers, you can author an interactive web page or standard reseach paper from Markdown source documents. This example article demonstrates the basic syntax and functionality of a Living Papers document. Compare the source markup and the resulting rendered web page! :::
Basic formatting includes:
- Inline text with italics, bold, or both.
- Inline code
x = Math.PI * r * r
. - Inline code with syntax highlighting
x = Math.PI * r * r
{.js}. - Inline math
$x = \pi * r^2$ . - Super^script^, sub
script, andstrikethrough. - Hyperlinks and [styled spans]{.underline .smaller}.
- Lists (like this one!)
Mathematical notation is specified using TeX syntax.
Inline math (math
blocks, and numbered equation
blocks are supported.
Compare a math
block:
And an equation
block:
\frac{a + b}{a} = \frac{a}{b} = \phi
::: table {#irrational .margin}
Symbol | Value |
---|---|
1.618033... | |
3.141519... | |
2.718282... |
| Some irrational numbers. :::
Basic images and tables can be included without adornment using standard Markdown.
To create numbered and captioned elements, place content within :::
-fenced figure
or table
blocks.
::: figure {#goldenratio .center}
| A depiction of the Golden Ratio (
Give credit where credit is due! Living Papers was inspired by earlier work by @doi:10.1145/3242587.3242600 on the Idyll language. Living Papers supports citations references with author name(s) or with reference number only [@doi:10.1145/3242587.3242600]. Citations are also interactive: click/tap a citation to view a pop-up with more information.
Citation information can be automatically retrieved using a unique ID:
- A DOI
[@doi:10.1145/3242587.3242600]
→ [@doi:10.1145/3242587.3242600] - A Semantic Scholar ID
[@s2id:4fca64e6dc4e803d3ed904c04c6845a9e6adc53e]
→ [@s2id:4fca64e6dc4e803d3ed904c04c6845a9e6adc53e]
Citations can also be defined in BibTeX format, either in an external file (listed under the references
key of the article metadata) or included anywhere in the document in a bibliography
block:
~~~ bibliography
@inproceedings{conlen2021,
title={Idyll Studio: A structured editor for authoring interactive \& data-driven articles},
author={Conlen, Matthew and Vo, Megan and Tan, Alan and Heer, Jeffrey},
booktitle={The 34th Annual ACM Symposium on User Interface Software and Technology},
pages={1--12},
year={2021}
}
~~~
For example, @conlen2021 (@conlen2021
) extends Idyll with a graphical structured editor to create interactive articles without writing markup code.
To create a list of citations [@doi:10.1145/3242587.3242600; @conlen2021], separate references with semi-colons: [@doi:10.1145/3242587.3242600; @conlen2021]
.
@inproceedings{conlen2021,
title={Idyll Studio: A structured editor for authoring interactive \& data-driven articles},
author={Conlen, Matthew and Vo, Megan and Tan, Alan and Heer, Jeffrey},
booktitle={The 34th Annual ACM Symposium on User Interface Software and Technology},
pages={1--12},
year={2021}
}
Cross-references use a syntax similar to citations:
- Figures:
@fig:teaser
→ @fig:teaser,@fig:goldenratio
→ @fig:goldenratio - Tables:
@tbl:irrational
→ @tbl:irrational - Equations:
@eqn:ratio
→ @eqn:ratio
By default, a descriptive prefix like "Figure" is included.^[To show a reference number only, wrap the reference like so: [-@fig:goldenratio]
→ [-@fig:goldenratio].]
import { Scrubber } from "@jheer/scrubber"
::: figure {.html:only}
viewof rot = Scrubber([0, 10], {
step: 0.05,
value: 5,
alternate: true,
label: 'Rotation',
format: x => x.toFixed(2)
})
viewof sep = Scrubber([1, 100], {
value: 4,
alternate: true,
label: 'Separation'
})
size = 25
---
n = Math.ceil((800 + 4*size) / sep)
---
pad = 30
---
color = d3.scaleSequential(d3.interpolateRainbow).domain([360, 0])
---
svg`<svg width="${800}" height="${2*size+pad}" viewBox="0 0 800 ${2*size+pad}">
${d3.range(0, n).map(i => {
const a = (i * rot) % 360;
return svg`<rect
transform="translate(${i*sep-2*size-1}, ${pad/2}) rotate(${-a}, ${size}, ${size})"
width="${2*size}" height="${2*size}"
fill="none" stroke="${color(a)}"></rect>`;
})}
</svg>`
| An interactive, explorable version of @fig:teaser. Can you deconstruct the shapes? :::
value = -10
---
max = 10
---
step = 0.1
---
sinc = x => x === 0 ? 1 : (Math.sin(Math.PI * x) / (Math.PI * x))
---
format = x => x.toFixed(2).replace('-0.00', '0.00')
::: figure { #graph .margin }
Plot.plot({
y: { grid: true },
marks: [
Plot.ruleX([value], { stroke: '#888' }),
Plot.line(
d3.range(-max, Math.max(max + step, value), step),
{ x: x => x, y: x => sinc(x), stroke: 'steelblue', strokeWidth: 2 }
),
Plot.dot([value], { x: x => x, y: x => sinc(x), fill: 'steelblue' })
],
width: 400,
height: 250
})
Scrubber([-max, max], {
step, labelWidth: '20px', loop: false,
label: tex`x`, format: x => x.toFixed(1)
})
| -max
max=max
step=step
title="Drag to adjust value" bind=value} → $$\sinc(x) =
Living Papers support interactive content using JavaScript code blocks and an extensible component system, all connected via a shared reactive runtime. The runtime automatically re-evaluates page content in response to interactive updates.
Living Papers uses the same JavaScript dialect as Observable notebooks. We can define variables, add input widgets, and generate figures (e.g., using Vega-Lite or Observable Plot, as in @fig:graph) just as we would in a notebook. We can also directly import content from public Observable notebooks, like this D3-based line chart of unemployment rates by U.S. county:
chartWidth = 775
---
import { chart as d3LineChart } with { chartWidth as width }
from '@d3/multi-line-chart'
---
d3LineChart
Living Papers also supports other types of content (not demonstrated here) through custom plugins.
One or more plugins can be specified under the plugins
article metadata key.
For example:
- The
knitr
plugin evaluates R code blocks (```r
) at compile time and includes the results in the output document (similar to RMarkdown). - The
pyodide
plugin evaluates Python code blocks (```py
) in the browser using Pyodide. These Python blocks follow the same reactive logic as Observable JavaScript, and computed Python results are directly accessible from JavaScript.