During the summer of 2016 I decided to simulate fireflies as a visual piece for my personal website; and as it transpired, it simultaneously lead to the progressive development of lampyridae.coffee – a lightweight CoffeeScript library that can be used to produce simple two-dimensional particle effects. As the namesake example of lampyridae.coffee, I thought I should elaborate on some of the aspects of mimicking Fireflies.
Figure 1. A photograph of Fireflies in a forest near Nuremberg (30 s exposure) [1].
As can be seen in figure 1, in any quick Google image search, or even outdoors, the humble Firefly is an enchanting winged beetle that can produce a variety of colours from its natural bioluminescence. Depending on the species, this can range from an assortment of eerie greens to a small ensemble of almost golden yellows.
Species | Peak Wavelength / nm |
---|---|
Pyrophorus plzgiophthalamus (dorsal organ) | 543.0 |
Photuris pennsylvanica | 552.4 |
Diphotus sp. | 555.0 |
Photuris jamaicensis | 555.0 |
Photinus pardalus | 560.0 |
Photinus pyralis | 562.1 |
Photinus commissus | 564.0 |
Photinus marginellus | 564.6 |
Photinus pallens | 565.0 |
Photinus xanthophotus ♀ | 567.0 |
Photinus leucopyge | 569.0 |
Lecontea sp. | 570.0 |
Photinus lobatus | 570.0 |
Photinus evanescens | 570.0 |
Photinus melanurus | 570.0 |
Photinus nothus | 570.0 |
Photinus (new species) | 570.0 |
Photinus morbosus-ceratus | 571.0 |
Photinus gracilobus | 572.0 |
Photinus scintillans ♀ | 574.8 |
Photinus scintillans ♂ | 575.1 |
Pyrophorus pliophthalamus (ventral organ) | 582.0 |
Table 1. The bioluminescent peak wavelengths from a sample of Firefly species [2].
More quantitatively, the data from table 1 can be used to show that the average peak wavelength emitted by this sample of species is 566.0 nm with a three-sigma deviation of 26.2 nm. Presuming we can accept that the majority of peak wavelengths fall within this deviation either side of the average – a good sample would imply a 99.7% coverage of values – then we can approximate these as suitable bounds from which to draw random colours. In other words, this means that we have a range of 539.8-592.2 nm from which we can select the colours of our Fireflies.
How do we convert these wavelengths of light to a computer readable colour code? There are a few methods of varying correctness; but as our values fall outside the purple region of the visible spectrum, one quick and approximate way is to consider these individual peaks as spectral – or more strictly as the dominant wavelengths from the bioluminescent spectra. This is an important assumption as a given spectrum may have many peak wavelengths but only one dominant wavelength; yet it is this latter quantity, together with its complementary, that forms our perception of hue [3].
Figure 2. Saturated hue scale in the HSB/HSL colour spaces. The unit of hue along the bottom is in degrees (°) [4].
Using the above consideration in addition to assuming that all the colours are saturated, the dominant wavelength can now be approximated as a colour code by mapping the visible spectrum to the hue scale in figure 2. This can be done with the equation,
$$ \begin{equation} \text{hue} \approx h_{\text{S}}\bigg(\frac{\lambda_{\text{max}} - \lambda}{\lambda_{\text{max}}-\lambda_{\text{min}}}\bigg) \label{eq1} \end{equation} $$where $h_{\text{S}}$ is the hue in degrees to use as a scaling factor, $\lambda_{\text{max}}$ the maximum wavelength of the visible spectrum, $\lambda_{\text{min}}$ the minimum wavelength of the visible spectrum, and $\lambda$ the wavelength to convert. To avoid the purple and repeating red region of the hue scale in figure 2, we will limit $h_{\text{S}}$ to the blue at 240 degrees. For the maximum and minimum wavelengths, I've selected the values of 650 nm and 436 nm respectively by comparing the approximate wavelengths of red and blue with their corresponding sRGB values and hues [5]. Finally, after all of that, plugging these values into eq.$\eqref{eq1}$, along with the wavelength bounds from earlier, should give an approximate maximum hue of 123.55 and a minimum hue of 64.86.
In summary, an approximately valid colour can be used in construction of the Fireflies using a web legal HSL colour code and a random number from the range 64.86-123.55 – for example, "hsl(72.3, 100%, 50%)"
. Of course, HSL is readily convertible to an RGB or hexadecimal code; both of which are supported by all major browsers.
As creating Fireflies was the original purpose of lampyridae.coffee, there is a specialised Firefly
class descended from the standard Particle
class already packaged within the library. Here is an edited extract from the library:
class Lampyridae.Firefly extends Lampyridae.Particle
### Construct and manage a Lampyridae firefly 'particle'.
#
# @param canvas [Object] Instance of Lampyridae.Canvas to attach the firefly to
#
# @option x [Number] Position of the firefly along the x-axis
# @option y [Number] Position of the firefly along the y-axis
# @option theta [Number] Direction of the firefly (radians anticlockwise from the x-axis)
# @option speed [Number] Speed of the firefly
# @option radius [Number] Radius of the firefly
# @option alpha [Number] Opacity of the firefly
# @option colour [Array] RGB Colour code array of the firefly - e.g. "[r, g, b]"
# @option bound [String] Type of bounding [none|hard|periodic]
###
constructor: (canvas, options) ->
options ?= {}
options.x ?= Lampyridae.rand 0, canvas.width()
... # More option setting goes here
super(canvas, options)
### Firefly class prototype parameters.
# Can be set by the user; e.g. Lampyridae.Firefly::radiusMax = 50, etc.
###
speedMin: 1
speedMax: 7
radiusMax: 3.0
radiusMin: 0.5
turningAngle: 0.1 * Math.PI
hueMax: 123.55 # Green
hueMin: 64.86 # Yellowy
saturation: '100%'
lightness: '50%'
opacity: 0.8
bound: "hard"
enableAlpha: true
### Random turn; set turngle angle to limit possibilities ###
randomTurn: () -> @turn @turningAngle * (2.0 * Math.random() - 1.0)
### Random walk with respect to the bounds of the canvas and draw the Firefly ###
update: () ->
unless @applyBounds() then @randomTurn()
@move()
@draw()
Without overriding the default options, the construction of a Firefly
sets a random parameter for each of the options specified in the comments; bounded by the prototype parameter members accessible to all instances of the class. This includes the selection of a random colour from the hue range calculated earlier. Other than specific parameter bounds; the Firefly
child class quintessentially introduces a randomTurn()
method that allows a restricted random walk to be simulated on each update()
of the object.
Using this class is fairly simple. In a separate CoffeeScript file the following extract, when compiled, will create and animate 25 Fireflies by drawing them onto a newly generated canvas
tag nested under the body element of a chosen document.
# Only the Canvas and base Particle classes are included by default
require 'particle/firefly'
# By default, if there is no existing canvas with the id 'world', this will
# attach '<canvas id="world"></canvas>' under the body element.
canvas = new Lampyridae.Canvas 'world'
Lampyridae.Firefly::speedMax = 5 # You can change proto parameters!
Lampyridae.Firefly::enableGlow = true # Glow is not enabled by default
Lampyridae.Firefly::glowFactor = 4 # Default is 4; rerun if changed
total = 25 # Number of fireflies to spawn
fireflies = [] # For keeping track of the fireflies
# Reusable firefly creator - remember to tweak the total if reused.
do createFireflies = () ->
for i in [0...total]
firefly = new Lampyridae.Firefly canvas
fireflies.push firefly
# An iterative update over the fireflies - remember to add it to the canvas!
updateFireflies = () -> fireflies[i].update() for i in [0...total]
###
# Lights, camera, action!
###
canvas.addUpdate canvas.draw.clear # If you want the screen to clear between frames
canvas.addUpdate updateFireflies # Update all the fireflies every frame
canvas.animate() # Animate the canvas screen
Sometimes, the quickest way to understand any code is to see a demo of it in action; so here is an up-to-date version of the Firefly example on CodePen:
See the Pen Generating Fireflies with lampyridae.coffee by Taylor Siviter (@siviter-t) on CodePen.
[1] Quit007. 2006. Photograph of Fireflies near Nuremberg. [GFDL, CC-BY-SA-3.0 or CC BY-SA 2.5-2.0-1.0]. URL: commons.wikimedia.org.
[2] Seliger, H. H., and McElroy, W. D. 1964. The Colors of Firefly Bioluminescence: Enzyme Configuration and Species Specificity. Proceedings of the National Academy of Sciences of the United States of America, 52(1), 75–81. DOI: 10.1073/pnas.52.1.75.
[3] Qi Yao, Jiaqi Ju, Rongqing Liang, Dahua Chen & Haitian Zhao. 2014. Relationship between Peak Wavelength and Dominant Wavelength of Light Sources Based on Vector-Based Dominant Wavelength Calculation Method, LEUKOS, 10(1), 11–18. DOI: 10.1080/15502724.2013.833823.
[4] Kalan. 2007. Hue scale image. [Public domain]. URL: commons.wikimedia.org.
[5] Wikipedia. 2016. Spectral color. [Online]. URL: en.wikipedia.org