Mushrooms are pretty amazing. Ancient. Weird. Scary. We walked around on a couple of days taking pictures of mushrooms. I took the geographical locations of each photo, and used these to display a simple scatter plot. Clicking on a point reveals the mushroom at that location. Taking the pictures was a fun collaborative activity, and the pictures reveal many weird and wonderful mushrooms.
This project incorporates p5.js into svelte using p5-svelte. It uses some things I learned with [Walk in the woods][woods].
]]>Tree Bark provides a bark-centric view of The Tree Atlas: South Central Region from the Ontario Ministry of Natural Resources. This is a great way to learn the trees you might see during A Walk in the Woods.
The project uses JavaScript and local storage to manage clicks on a “That’s my tree!” button, as well as the CSS ‘flex’ display to easily switch between mobile and larger device layouts. Google Gemini did much of the initial work.
]]>An ‘unc’ is, apparently, a term used by the younger peoples to refer to older individuals with unappreciated (by the younger peoples) wisdom. This project looks at our family ancestry using information compiled by other family members.
I learned quite a bit about the svelte framework, data encryption using sodium (with R’s sodium and JavaScript’s libsodium.js), and the packages DataTables.js, graphology, and Sigma.js.
]]>The Movies started because I wanted to explore WASM (web assembly) to run more typically server-side applications in the client’s browser. An interesting example is the SQLite WASM database. The New York Times came out with a list of The 100 Best Movies of the 21st Century as chosen by ‘creators’ (producers, directors, actors, etc.). As we started to watch the movies, it became hard to remember details and even our reaction, and actually the page provided by the New York Times is relatively hard to navigate (e.g., no simple way to find the 34th movie, the display jumping around as the page renders, etc.).
So we start with a simple table, then add links to reviews and where-to-watch. Then provide information on directors, writers, and actors of each movie from The Movie Database (TMDB), and finally write our own notes on the movies that we’ve seen.
It turns out to be a relatively complicated project, and a good way to level up my JavaScript skills. It has also been a great way to be amazed by the bold new age of AI-assisted coding.
]]>Day Lilies is a light-weight example summarizing the flowering phenology of a small patch of day lilies in our front yard, using the DataTables and Plotly.js libraries.
]]>A Walk in the Woods continues my JavaScript exploration. It uses p5 to load and display images of tree trunks taken while while walking through the woods on our propoerty near Caledon East, Ontario.
]]>(updated 2024-12-30)
This continues my exploration of JavaScript.
Samantha Harvey says her book Orbital tries to be a space pastoral, describing the relationships of six space station astronauts during the sixteen orbits of a single earth day. There is no threatening destruction or steamy romance, just an encounter between Earth and its Anthropocene exhabitants.
The frontispiece of Orbital shows the orbits of the space station during this day; it is fun to think about orbits. For instance, daylight is not restricted to the northern hemisphere!
A circular orbit is a path where an object (e.g., space station) moves around a central body (e.g., the earth) in a perfect circle. An equatorial orbit is an orbit that is directly above the equator. An inclined orbit is tilted relative to the equator. The space station has an inclined orbit, where the inclination is 51.6°. This is the angle that each orbit crosses the equator.
If the earth were stationary, the space station would always traverse the globe along a single circumference. But the earth rotates, so each time the space station completes an orbit, its location over the ground has changed. The amount of change is the amount that the earth has rotated during the space station’s orbit. This is why the orbits are offset in the frontispiece above.
The altitude of a satellite orbit occurs when gravitational force pulling the satellite toward the earth is balanced by the centripetal force caused by the speed of the satellite in orbit. Since satellite mass is a linear term in both the gravitational and centripetal equations, the actual mass of the satellite does not determine orbit. Geostationary satellites are much further from the earth (35,800 km) than the space station (in low earth orbit, about 410 km). Satellites in more distance (e.g., geostationary) orbits move slower than satellites closer to the earth (e.g., the space station or StarLink communication satellites) because satellites closer to the earth have to overcome the stronger force of Earth’s gravity.
There are a lot of satellites (and other objects) in space. Here are the almost 7,000 Starlink satellites.
This answer on the Space Exploration StackExchange provides basic math required. There are three steps.
The first step is to describe movement on a sphere with an inclined orbit.
where the radius of the orbit is 1, \(\omega = 2 \pi / T\) is the circumference of the sphere scaled by the orbital period \(T\), and \(i\) the inclination of the orbit. \(t\) is the time since the start of the orbit.
The next step is to translate the three-dimensional coordinates to two-dimensional latitude and longitude. These equations assume that the orbit starts at longitude 0.
Incorporate the rotation of the earth by subtracting the scaled orbital period of the earth; only the longitude is influenced by the rotating earth.
\(\omega_E = 2 \pi / T_E\) and \(T_E\) is the orbital period of the earth.
We use minutes as the unit of time. The orbital period of the space station \(T\)varies between 90 and 93 minutes; we’ll use 92 minutes. The orbital period of the earth \(T_E\) is approximately 1436 minutes (23 hours 56 minutes and 4 seconds). The inclination of the space station orbit is approximately 51.6°.
A more realistic calculation might use two-line element set notation to characterize the location and movement of a satellites at a particular point in time, coupled with simplified perturbation models to calculate orbital state vectors. satellites-js or ootk are two JavaScript implementations.
The first sketch show a flattened earth, with space station orbits superimposed. The orbits are described by equations for \(lat, lon\) from the previous section. The orbits appear offset in space. But actually the orbits are constant, it is the earth that rotates under the space station. The space station dots are equally spaced in time, but a close observation shows that they are further apart at the largest latitudes; this is because the projection of the spherical earth into two dimensions stretches larger latitudes more than smaller latitudes.
The second sketch shows the rotating earth, inclined 23.5°. The space station orbit is constant, always intersecting the equator in the middle of the image. The location of the space station is described the equations for \(x, y, z\) above. The orbit is inclined 51.6° from the equator. Space station markers move to the right in synchrony with the rotating earth; it took me a long time to figure out how to make this simple transformation in p5!. The earth’s radius is 6378 km, and the orbit is 400 km above the earth. Illumination is from the front and left, as would occur somewhere between the spring (June) solstice and summer (March) equinox; the image of the earth, from NASA, is generated from November data, so is not quite correct.
An abstraction has the earth’s equator, orbit, and the location of the visitor’s internet access. The earth is illuminated from the top so that ‘daylight is in the northern hemisphere’. The space station orbit pulses at 92 beats per minute. The axial tilt of the earth is a little akimbo.
The implementation uses p5; here’s the JavaScript. I use
‘WEBGL’ mode for 3D rendering. A difference between WEBGL and P2D
rendering is the the WEBGL coordinate system has the center of the
canvas as the origin, versus the top left under P2D. Also objects in
WEBGL are are always centered at 0; locations in the canvas are
determined using operations such as translate() and rotateY() to
adjust the relative coordinate system before drawing the
object. push() and pop() are used to limit the effects of
translate() etc., to sepcific shapes.
This post uses MathJax for rendering mathematical equations; see this helpful StackOverflow post. I used Rodriques’ rotation formula from this StackOverflow answer to rotate the light source around the Earth.
]]>I respond to the horizon and vertical structure in Martin’s work. I interpret the horizon as prarie, and the weft and warp starting with Martin’s relationship with Lenore Tawney. Martin’s work is subtle and poorly captured on the internet. Here is ‘The Tree (1965)’ from the Buffalo AKG.
That gray canvas is penciled lines bouncing over a textured background. Here’s a close-up (trigger warning: please move away from the art; also, do not lick the art).

I like how this is so structured, but not perfect or mechanical. Textures bump the line away from straight. Lines end but not at the canvas. Etc. It is very hard to reconcile the color of the AKG and iPhone images, and my own direct experience (dirty [with age or work] ‘white’).
This first iteration aims to reproduce the basic structure of the art. I hope to explore texture and more subtle line drawing in a subsequent post.
I imagine that the lines are drawn as warp and then weft. The practice is contemplative, so the lines are drawn slowly.
The implementation uses p5. I explore JavaScript classes (Line
and subclasses VerticalLine, HorizontalLine; here’s the
JavaScript) as well as different ways to represent color. I’m using
‘HSB’ here, where the ‘H’ (hue) is easily interpreted as location on
a color wheel.
The Buffalo AKG Electric Op exhibit reminded me of an old folly, and this is a second iteration. Briefly, I am using a simple population genetics model of genetic drift with infinite alleles as an excuse to learn a bit more JavaScript, including the p5 library.
The previous iteration used an individual-based simulation to follow a population of individuals over time. The innovation here is to follow the small number of segregating alleles, rather than the 1000’s of individuals in the population. The simulation is both smaller and faster, so more realistic population sizes can be used. The simulation may still have some artistic merit…
The population size N is 2-10 times larger (depending on the width of the display, as a proxy for processor speed), and the mutation rate μ 10 times smaller, than in the individual-based simulation. The number of generations per frame is increased four-fold to 200, and the frame rate doubled to 24 (200 x 24 = 4800 generations) per second (available CPU may limit the rate of display; this animation is slow on mobile devices). As before, ‘Segregating’ alleles are just the number of alleles in the population at each generation. ‘Replacements’ summarize how many times a common allele has been replaced by a new allele.
Although the figure is ‘mathematical’ and in that sense contrasts with Mondrian’s carefully composed paintings, it is useful to reflect on decisions made during development.
The bubble chart representation is much less frenetic than the grid used in the individual-based simulation (I could have summarized the individual-based simulations using bubble charts, but then the connection to Mondrian would be even less clear).
Originally I set the maximum bubble diameter to 1/2 the minimum width or height of the canvas. This meant that new alleles were centered in a two-dimensional square on the canvas. The simulation above sets the maximum diameter to the minimum width or height, so when only a single allele is present its bubble occupies the entire width or height of the canvas. A corollary is that new alleles are constrained to a vertical (if the width of the canvas is smaller than the height, as on a mobile device) or horizontal (width larger than height, as in a browser) line. This further simplifies the bubble chart dynamics, with the eye needing to cope with fewer changes across frames.
The background color was originally white, but switching to black is easier on the eyes and seems to better contrast, in general, with the randomly chosen allele colors.
The alleles were originally drawn without an explicit stroke for the circumference. Drawing the circumference provides a more defined shape for the eye to perceive. This is particularly important with partially transparent bubbles, and transparent bubbles are needed to visualize overlapping alleles.
In addition to p5, I use binomial() from stdlib. Here’s the
Boogie Woogie bubble JavaScript.
The script is written using p5’s ‘instance’ mode, and as a JavaScript
module. Using instance mode and modules loads only p5 variables into
the global namespace, not stdlib symbols or symbols used in the Boogie
Woogie script. Instance mode involves writing a closure (function)
that is passed to the p5() constructor. Simulation parameters and
functions are defined (currently) in the closure rather than at the
level of the module, resulting in better encapsulation. The stdlib
symbol binomial is imported into the module, more closely coupling
the import to its use. Modules also allow more careful validation,
flagging a couple of instances where variables were used without let
or const declarations. Using p5 instances also allows for more than
one canvas on a page.
The Buffalo AKG Electric Op exhibit reminded me of an old folly (see also MoMA’s Rafaël Rozendaal Light).
A Piet Mondrian exhibit at MoMA or the National Gallery in 1994-1995 helped me to understand a little bit about modern art; there’s a catalog available. The exhibit was arranged chronologically, so one could see Mondrian start with pictorial representations (e.g., The Weavers’ House, Winterswijk, 1899; p. 96 of the catalog), through impressionist (Mollen; Mill in Sunlight, 1908, p. 106) and cubist (Still Life with Gingerpot II, 1912, p. 130; Tableau No. 2; Composition No. VII, 1913, p. 146) renderings before landing on the geometrical representations (Composition A; Composition with Black, Red, Gray, Yellow, and Blue, 1920, p. 197) that define most of the last 20 years of Mondrian’s life. One of the last works in the exhibit was Broadway Boogie Woogie (1942-1943, p. 297) where Mondrian seems to relax his tight structure to introduce an almost dynamic and playful component; apparently the image is inspired by traffic and lights on Broadway in New York City, enlivened by the music of the blues.
The main theme I took from the exhibit was Mondrian’s desire to identify universal values that transcend reality. Thus simplifying representation to primary colors and geometric shapes, and then to assemble these colors and shapes into aesthetic meaning.
Simplified representations are often used in mathematical models, and in particular in the ‘infinite alleles’ model describing genetic drift in population genetics. The infinite alleles model imagines a population of individuals, represented by the alleles at a particular genetic locus. Alleles experience mutation to a novel form. The fate of the allele (whether and how quickly it is lost from the population, for instance) is determined by purely stochastic processes.
There is a kind of dynamic equilibrium between mutation and drift. Often a single allele dominates, with a small number of recently introduced mutations at low frequency. Sometimes the dominant allele is replaced by a new allele. I imagined representing a population by a grid of squares, each square colored to indicate the allele. Genetic drift is visualized by updating the grid to reflect new mutation and sampling between generations. The dynamics of new alleles reminded me of flickering lights, and of Mondrian’s final paintings.
I explored simulations of genetic drift while teaching in the 90’s, and eventually included the art history and simulation in a single memorable (for me, anyway) lecture in a graduate Population Genetics course at Washington State University.
It’s easy to simulate the infinite alleles model (literally two lines of R code, for instance), making it a fun use case for exploring new programming languages.
Several of the Electric Op works used the ‘Processing’ programming language for computer-based visualization. A modern implementation is in the p5 javascript library. A perfect way for me to learn a bit more javascript (my use of p5 is very rudimentary)! Here is the result:
The population is represented by a 71 x 71 grid of alleles, so 5041 haploid ‘individuals’ (I started with a rectangle of 5000 individuals, but then adjusted to match the square format of Mondrian’s work). Initially all alleles are the same (random) color. Each generation introduces new mutations (new colors) at rate μ, and then sampling with replacement the entire population. The display is updated every 50 generations, at a maximum rate of 12 frames (50 x 12 = 600 generations) per second. Progress is reported in number of generations scaled by population size (e.g., t / N = 1 indicates 5000 generations). ‘Segregating’ alleles are just the number of alleles in the population at each generation. ‘Replacements’ summarize how many times a common allele has been replaced by a new allele.
There is a lot of activity in the display. New alleles flicker on and off fairly rapidly, perhaps existing for only a few frames. The display becomes quite animated when a couple of alleles become more equally frequent. But replacements are pretty rare, maybe once every 10 time units, and with a lot of variability. It’s tempting to feel that a new mutation that has become frequent will ‘win’, but actually the chance of winning is just the frequency of the allele in the population, independent of whether the allele has recently increased in frequency or is new, etc. Hopefully this provides some entertainment and maybe a bit of education.
See the companion Bubbles post.
In addition to p5, I use the stdlib javascript library (for
binomial random deviates, and to sample the population). I also
needed to learn how to integrate a p5 canvas into the jekyll static
site generator (adding an ‘assets’ folder to the root of
the site with javascript scripts and libraries, and inserting
<script> and <div> tags into the markdown for this page). Here’s
the Boogie Woogie JavaScript.
Population genetics and WSU remind me of Richard Gomulkiewicz, to whom I dedicate this post.
]]>