Some curious explorations in mathematics and wildlife, by Ashutosh Shinde.
The whole background is drawn live in code on a blackboard: chalk equations, animals and a stick-figure explorer. There are no videos or background images. Each section of the home page has its own scene. Scrolling to the next section dissolves one scene into chalk dust that re-forms as the next, and the scroll position is the timeline: stop half-way and you see the half-way state.
cd ashutosh-shinde.github.io
python3 -m http.server 8000
Then open http://localhost:8000. Use a local server: opening index.html by
double-click blocks the fonts.
| What | File |
|---|---|
| Home page text, links, section texts, the comment box | index.html |
| About page: the words, photo, honours, In the field, thesis and projects, personal projects, skill cards | about/index.html + css/about.css; the skill cards (turning over, sketches, rolling names): js/about.js |
| About page: My time series (every line, its dates, rows, card notes and photos) | about/life.js (how to edit is at its top; card photos go in about/photos/); the drawing: js/lib/lifeline.js |
Blogs: title and the list of stories (the boxes are made by tools/publish/build.py) |
blogs/index.html |
| Blogs list: the background of names (Himalayan peaks and routes, glaciers, India's parks and sanctuaries: particles in a box) and the explorer strip at the bottom | js/scenes/gearbox.js, js/lib/strip.js |
| Simulations: the list of collections and their numbered cards | simulations/index.html |
| A simulation page (one folder each), e.g. the discrete-time logistic model | simulations/population-dynamics/discrete-logistic/index.html |
| A simulation's maths and controls (one file per simulation) | js/sims/, e.g. js/sims/logistic.js (tested by node tools/test-logistic.mjs) |
| Card 2: the continuous-time logistic model | simulations/population-dynamics/continuous-logistic/ + js/sims/continuous-logistic.js (tested by node tools/test-continuous-logistic.mjs) |
| Card 3: harvesting with Holling responses | simulations/population-dynamics/harvesting/ + js/sims/harvesting.js (tested by node tools/test-harvesting.mjs) |
| Card 4: Lotka–Volterra competition | simulations/population-dynamics/lv-competition/ + js/sims/lv-competition.js (tested by node tools/test-lv-competition.mjs) |
| Card 5: the consumer–resource model | simulations/population-dynamics/consumer-resource/ + js/sims/consumer-resource.js (tested by node tools/test-consumer-resource.mjs) |
| Card 6: prey–predator models | simulations/population-dynamics/prey-predator/ + js/sims/prey-predator.js (tested by node tools/test-prey-predator.mjs) |
| Plots in chalk style (axes, lines, legends, log axes) | js/lib/plot.js |
| ODE solvers: Dormand–Prince, ROS2 for stiff models, 2×2 eigenvalues | js/lib/ode.js (tested by node tools/test-ode.mjs) |
| Shared page helpers for the simulations: sliders, boxes, runtime checks | js/lib/simkit.js |
| Checking tools: screenshots, slider sweeps, error pass, page changes, preview | tools/dev/ (see its README) |
Elementary Mathematics: title and the list of chapters (the boxes are made by tools/publish/build.py) |
elementary-mathematics/index.html |
| Blog posts and maths chapters: what you write (Google Doc, Markdown, R Markdown, LaTeX, knitted HTML) | content/blog/, content/maths/ — how to write and publish: content/README.md |
| The pages made from them (don't edit: they are rewritten on every publish) | blogs/<name>/, elementary-mathematics/<name>/ |
| Publishing: files → pages, maths typeset (MathJax), photos resized; the page templates | tools/publish/build.py, tools/publish/templates/; run on GitHub by .github/workflows/publish.yml |
| Drive → GitHub sync (Google Apps Script) | tools/drive-sync/drive-to-github.gs (tested by node tools/test-drive-sync.cjs) |
| Styles of the two lists, the posts and chapters, and the strip | css/writing.css |
| Comments page | comments/index.html |
| Colours, fonts, sizes, where each section's text sits | css/site.css (tokens at the top) |
| The scroll / dust / calm-zone / page-change engine | js/engine/engine.js (settings in CFG) |
| The six home-page scenes | js/scenes/home.js, simulations.js, elementary.js, lab.js, blog.js, feedback.js |
| Page backgrounds: drifting pure maths (Elementary Mathematics), snow on grass (chapters), mountains over a sea of clouds with an eagle, faint snow and the grass (blog posts) | js/scenes/pure.js, js/scenes/snowfield.js, js/scenes/summit.js |
| Falling chalk snow (Feedback, Comments, chapters) | js/lib/snowfall.js |
| Animals (hare, wolf, deer, tiger, leopard, owl, ibex, snow leopard, eye) | js/lib/creatures.js |
| The explorer (stick figure with glasses and backpack) | js/lib/explorer.js |
| Chalk equations, chalk lines, blackboard texture | js/lib/chalk.js + js/data/equations.js and js/data/pure.js (both generated) |
| Fonts (small subsets) and their licences | fonts/ + fonts/LICENSES.txt |
| Comment storage (Google Sheet) | js/comments.js + tools/comments-google-apps-script.gs |
| Old site | trash/ (not linked anywhere) |
Every file starts with a comment that explains what it does and has a HOW TO EDIT note listing the settings worth changing.
- Text and links: edit
index.html. Every section is marked with a comment banner, for example<!-- ===== 2 · SIMULATIONS -->. - Fill a section page: open its
index.htmland write below the title. - Turn on comments: follow the steps at the top of
tools/comments-google-apps-script.gs. It takes about 10 minutes. Then paste the web-app URL intoSHEET_URLinjs/comments.js. To remove a comment, delete its row in the sheet. - Add or change an equation:
- Edit
ITEMSintools/make_equations.py. - Run
python3 tools/make_equations.py. This needs LaTeX (pdflatexandpdftocairo). - Use the new id in a scene.
- Edit
- Add or change a drifting note on the Elementary Mathematics page: edit
PUREintools/make_equations.pyand runpython3 tools/make_equations.py pure. Every entry appears on the page by itself. Ids starting withn_are theorem names, the rest are formulas. - Publish a blog post or a maths chapter: write it in Google Docs (or as
a
.md,.Rmd,.texor knitted.htmlfile), start it with the four header lines (title:,date:,category:,abstract:), and put it in your Drive folderWildQuant website/Blogor…/Elementary Mathematics, or upload it tocontent/blog/orcontent/maths/on github.com. It gets its page and its box on the list by itself. All of it:content/README.md. - The Blogs background and strip: the peaks, routes, glaciers, parks and
words are the lists at the top of
js/scenes/gearbox.js; the explorer's activities areACTSinjs/lib/strip.js(the wall's height, how concave it is and the lake's length are inWALL). - The blog posts' background:
js/scenes/summit.js(the mountains, the eagle, how faint the snow is). Posts are made fromtools/publish/templates/post.html, which loads it. - Add a simulation:
- Copy a simulation folder to a new folder and write the page:
simulations/population-dynamics/discrete-logistic/for a map, or one of cards 3–6 (e.g.harvesting/) for differential equations. Those pages also load the ODE solvers (js/lib/ode.js,WQ.ODE) and the shared sliders and boxes (js/lib/simkit.js,WQ.Sim). - Put its maths and wiring in
js/sims/<name>.js, drawing withWQ.Plot(js/lib/plot.js), and load the scripts in the page beforesite.js. Test the maths with atools/test-<name>.mjsand check the page with the tools intools/dev/. - In
simulations/index.html, add its card: copy an<li>and give it the next number and id (a comment there shows how).
- Copy a simulation folder to a new folder and write the page:
- Maths in a post or chapter: LaTeX in its file (
$…$,$$…$$), seecontent/README.md. Maths in a hand-made page (set in Latin Modern, like LaTeX):- inline:
<span class="m"><i>f</i> : ℕ → ℝ</span>, with letters in<i>and<sub>/<sup>for indices and powers - root:
<span class="m">√<span class="rt">2</span></span> - fraction:
<span class="frac"><span>1</span><span>2</span></span> - on its own centred line:
<p class="math"><span class="m">…</span></p> - theorem and proof:
<div class="theorem">…</div>,<div class="proof">…<span class="qed">∎</span></div> - the maths font has
ℕ ℚ ℝ ℤ ∅ ∈ ∉ ∋ ∌ ⊂ ⊃ ⊆ ⊇ ∪ ∩ → ↦ ⇒ ⇔ ∀ ∃ ≤ ≥ ≠ ≡ ∞ √ ℵ − × · … ⋯ ∖ ∣ ∎ 𝒫 𝜋 𝜑 𝜀 𝛿; other symbols use the text font.
- inline:
- Timing of a scene: the timeline constants (
T = {...}) are at the top of each scene file. - Speed and quality:
CFGinjs/engine/engine.jssets the pixel budget and the number of particles. The engine also lowers quality by itself on slow devices.
What you write lives in content/ (from Drive or uploaded on GitHub); every
push to master runs .github/workflows/publish.yml, which makes the pages
with tools/publish/build.py, commits them, and deploys the site to GitHub
Pages (about two minutes). One-time settings: GitHub → Settings → Pages →
Source: GitHub Actions; and the Drive sync script (steps at the top of
tools/drive-sync/drive-to-github.gs).
To make the pages on your computer: install pandoc 3.9, then
pip install -r tools/publish/requirements.txt,
npm install --prefix tools/publish, and run
python3 tools/publish/build.py (then preview as above).
Every page of the site opens and closes with the same animation: a click on a
link pulls the drawings into the link, and the next page opens under a quick
flash from the same spot. A new page gets this, and the blackboard, by being
built like the others. The easiest way is to copy a page and change it: copy
about/index.html for a plain page, or a chapter for a long read. If you
build one from scratch, it needs all of these:
- In
<head>:../css/site.cssand the small inline<script>marked "arriving from another page of the site" (copy it unchanged from any page). Then the scripts../js/lib/core.js,../js/lib/chalk.js,../js/engine/engine.jsand, last,../js/site.js, each withdefer. - At the top of
<body>: the two canvases<canvas id="board-tex" aria-hidden="true"></canvas>and<canvas id="board-fx" aria-hidden="true"></canvas>, and a back link<a class="back" href="../" data-collapse data-back="#home"><span aria-hidden="true">←</span> WildQuant</a>.data-backmakes it a back link: it returns to where the visitor left the home page, or to the section it names (here#home, the top) if they never were there. Use the section that links to your page. data-collapseon every link to another page of the site, for example<a href="../about/index.html" data-collapse>. Links to other websites, and#links within a page, do without.data-calmon the text blocks, so the drawings fade behind them.- A background (optional): add
data-scene="…"to<body>and load the scene's script afterengine.jsand beforesite.js.pure(faint drifting maths, also needs../js/data/pure.jsfirst),snowfield(snow on grass, also needs../js/lib/snowfall.jsfirst) andsummit(mountains, an eagle, snow and grass; also needssnowfall.js,explorer.jsandsnowfield.jsfirst) are made for pages. Without a scene the page shows the plain blackboard.
Paths are relative to the page's folder: ../ from a page one folder deep
(like about/), ../../ from two folders deep (like a chapter), ../../../
from three (like a simulation).
- Home intro: it plays once per browser session. Scrolling skips it, and
so does opening the page further down (a link like
/#feedback, or a back link to where the visitor left). - Reduced motion: visitors with "reduce motion" switched on see one still drawing per section. Nothing animates for them.
- Phones: they get the same scenes with fewer particles, a smaller flock, and layouts arranged for portrait.
- Calm zones: any element with
data-calmkeeps the drawings behind it faint. Usedata-calm="0.6"for a lighter effect. Animals are never faded: scenes draw them after the calm zones (the "top" pass) and size and place them so they stay clear of the text on any screen shape. - Page changes: any link with
data-collapsepulls the drawings into the link in a third of a second (the next page is fetched at the click and opened as the swirl closes), and the next page shows at once under a quick flash from the same spot; its drawings fade in as soon as the board has started. Every page works this way (see "Adding a page"). A page opens at its top, or at the#sectionthe link names; a back link (data-back) opens it where the visitor left it instead. The small script at the top of every page does this. Timing:collapseandnavAtinCFG(js/engine/engine.js); the flash is.wq-arriveand the fade-in.wq-ready #board-fxincss/site.css. - Starting quickly: a page shows its text first and starts the board right after. Only the scenes on screen are made at once; the others are made a moment later while the page is idle (on the home page with its intro, all at once, so that nothing interrupts the intro).