Building Electron Applications with Multiple Pages

If you don’t want to dive into webpack/framework build frame, but need an easy way to use multiple pages in electron with simple navigation, this might work for you.

There are a few approaches to handling multiple pages in an electron app. Since chromium comes bundled with electron, essentially a web browser, and electron runs a node application, one approach is just to serve multiple pages as external html files. The same way you might on a web server. You can load these external pages, locally or remotely, into the current BrowserWindow via links or api calls. But results aren’t as seamless as an application. There’s a notorious screen flash that happens sometimes when the external page loads. Though mitigated somewhat with tweaks, electron is really designed for single page/cached content applications.

Multiple pages can be implemented through views, available in various frameworks with architectures for routing and templating, like Vue, React and Angular. These assemble pages by building custom objects loaded into memory. But if we don’t want to use a framework with their attendant complexities, and don’t want janky window flashing from hard links, there’s a third alternative.

Deep in the source for https://github.com/electron/electron-api-demos lies a useful approach. I decided to simplify, deconstruct and generalize it here. I’ll also integrate Bootstrap 4 to leverage its tab components to do the actual navigation. Because, lazy.

To build from scratch:

1. git clone https://github.com/electron/electron-quick-start
2. cd electron-quick-start
3. npm install bootstrap
4. npm install jquery
5. npm install popper.js
6. npm install

Now, in the head of index.html add:

In the script section of the same index.html add:

Now the electron app is fully wired for bootstrap. Next we’ll create external templates for each of our pages. Here’s an example of a template for the main page created in an external file called main.html:

Create one of these files for each page in the application. Ensure that each has its own unique id and data-tab value. Next let’s go back and tweak the index.html to reference the templates. In the head section of the html:

for as many template pages as we created.

Next add the following to the script block in the same index.html file:

What this script fragment does is look for every import link in the head section, which it then loads and caches in memory as a document fragment and appends to an html element in the dom with an id that matches the id referenced by the data-tab in the template. Templates are a very cool, seldom utilized, feature baked into HTML5.

So all that’s left is replacing the body of index.html with the following to provide navigation and some slots and scaffolding for the pages.

Now run:

and voila. A multipage electron application using HTML5 templates and bootstrap for easy styling and navigation.

Complete source code available on https://github.com/kentskyo/spa-electron-bootstrap

(For a quickstart on electron in general, along with a solid discussion of options for packaging, updating and software certificates, I found this concise resource very well put together: https://www.learnelectron.com)

 

Equations of Form

Some people like zoos or art exhibits, I like to explore abstract species of thought. Yesterday I spent the afternoon roaming around L-systems.

L-systems are little self-contained universes of alphabets, axioms, and rules. The alphabets are building blocks and may be any symbols or letters imaginable. The axiom is an initial combination of those letters and the rules change the axiom, morphing it into something else generation by generation. This simple trifecta creates an infinity of forms.

L-systems are deterministic and linear. This means given a certain starting point and a certain set of rules, they will always produce the same results. They don’t have the ability of chaotic systems to veer off in unexpected directions, although there may be ways of coaxing them to do so depending on how their results are interpreted. Part of their algorithmic DNA is recursion. A fractal reflexiveness that applies rules to the results of rules in an infinite spiral.

We can map L-systems graphically and they can produce intricate patterns with an affinity to the natural world: leaves, trees, clouds, terrain. But they don’t have to be interpreted graphically. They could be mapped to poetry, music, rhythm, dance.

Here’s the relevant Processing code that interpreted the L-system for the graphic above:

Inspiration for this came from Dan’s. always entertaining and educational, Coding Challenges; an even deeper dive can be found here.

The Art of Particles

Particle systems first appeared in 1982. A special effect artist, Bill Reeves, used them in Star Trek II: The Wrath of Kahn for a scene called the “genesis effect.”

In essence a particle system is just an algorithm controlling particle creation and interaction. In practice this normally consists of an emitter, usually invisible, positioned somewhere in the world and tiny graphic elements (particles) the emitter spits out from its hidden position, all at once or in intervals.

By varying how, when and where of particle creation and what forces act upon them, we can simulate a variety of real-world effects like fire, smoke and rain. In theory any physical phenomenon could be simulated by a particle system since all matter is basically made up of particles (atoms.) However, the computational cost of juggling even a few thousand particles rapidly becomes a bottleneck. But with a modest number of particles, and clever coding, one can still produce cool effects, even in a lowly web browser.

For code geeks, particle systems are great projects to make from scratch, but this post is focused on their use in generative art. We will leverage Phaser, an engine used to create HTML5 games, primarily because it comes with a built in particle system and physics engine (useful for applying forces to the particles, like gravity and acceleration.) You could also experiment with these systems in Processing and Daniel Shiffman has some great tutorials on a simple prototype.

Here’s a self-contained Phaser scaffold to kick off experiments:

Phaser looks for function names to call at specific times. The function preload() is called for loading images and assets, create() is then called after all assets are loaded. After that, if update() and render() are defined, they are called in the standard game loop pattern at the maximum browser framerate.

Creating particles with this scaffold is easy. We’ll just load a small image to serve as the particle and define an emitter with some parameters to shoot copies onto the scene:

Particles will either explode from the emitter all at once, like a firework, in which case:

  • explode = true
  • lifespan = how long the particles “live” in milliseconds, after which they dissappear from screen
  • frequency has no effect if explode = true
  • quantity is how many particles are created during the explosion, up to amount in maxParticles

For stream from the emitter in a continuous flow of particles:

  • explode = false
  • frequency = interval in milliseconds between firing particles
  • quantity = 0 means keep firing particles indefinitely at the frequency specified, if maxParticles are reached, wait until one dies before firing another to take its place; if quantity = n just fire n particles total and then stop

Emitters have many additional properties to tweak besides what’s set in the start parameter. For example:

  • minParticleScale, maxParticleScale sets the range between the particle is randomly scaled when created
  • setAlpha(0.1, 0.9); as an example, sets the range of the transparency applied to the particle
  • width and height set the the area in which particles emerge, by default this is a single point, like a cannon, but it can be expanded to any size and the particle will emerge from a random location within this area
  • minParticleSpeed, maxParticleSpeed control a range of velocities each particle is assigned

Here’s a quick technique for masking images in Phaser:

Which creates a mask that be applied to any image, essentially “clipping” what’s displayed of the image to just the size and shape of the mask. That’s all the elements needed to create the following project:

See the Pen There’s Bugs in My Code by Kentskyo (@kentskyo) on CodePen.0

Canvas – Animation

Outside of the canvas api but part of HTML5, the widely supported requestAnimationFrame method replaces the old setInterval() and setTimeout() functions of the past for browser animation. A basic setup is as follows:

The animate function fires at whatever the browser determines is an optimal framerate, generally 60 times a second. If a browser tab running animate code is not visible, it may not be called at all (chrome), or may be called once a second (firefox); the actual behavior of browsers in this situation is unspecified. A timestamp is passed as an argument to the callback routine set to a value from Performance.now()  at the time the callback was triggered.

With this timestamp value,  we can calculate effective frames per second and make adjustments to ensure animations run at a constant rate. Or we can use elapsed time to perform actions once a second or in intervals other than the framerate.

To move at a certain number of pixels per second, regardless of the framerate:

Where ball.vx and ball.vy = the number of pixels per second.

To finesse the timing of a sweeping second-hand, we can calculate the number of radians traversed per second by chopping a circle up into 60 segments: 2PI/60 and then increment the angle based on how much of the second has elapsed:

To start the second-hand at a specific angle, like current seconds, it can be initialized like so:

This adjusts for the fact that radian = 0 is actually at the 3 o’clock position of a circle, or 90 degrees.

Instead of hardcoding 60, any divisor from 0..n could be used to map the value n to its corresponding location on the circle.

The following experiment animates the second-hand as a line from the center to its time corrected position along the circumference each frame. This creates a smooth, sweeping movement. Another function plots the location of the current hour, displaying it as a translucent circle and displays the minutes numerically inside.

(Using text with canvas wasn’t covered yet, but is explained well here. Note also that two canvas elements are used and stacked one upon another using css. This allows the dial to be drawn once to improve performance, since it doesn’t change, and leverages the default transparency of a canvas.

See the Pen Clock by Kentskyo (@kentskyo) on CodePen.0

Canvas – Drawing Bamboo with Code

After drawing rectangles, lines, circles (arcs) and triangles, about all that’s left in the toy box is the curve. Canvas has two types of curves: quadratic and cubic; they differ only in the amount of control points used for bending the line.

Quadratic curves have two anchor points and one control point and curves in only one direction. The more complex cubic curve adds another control point and can curve in two directions.

Curves can be interwoven when drawing paths with lines and arcs, and they pick up wherever the current context point is (or from a explicit moveTo which established a new context point):

where cpx and cpy is the control point and x,y is the endpoint

Playing with this:

The code draws two curves connected to each other (then endpoint on the second was the starting point on the first curve); the second set of curves calls scale(-1,1) before repeating the same calls again and creates a mirror image, which results:

eyes1

 

 

Using curves for eyes and leaves in the next project here’s an experiment constructing a bamboo grove. Each time the canvas is clicked it regenerates a new grove.

See the Pen Haunted Bamboo by Kentskyo (@kentskyo) on CodePen.0

 

 

 

 

Canvas – Triangles

There’s no built in function in canvas for making triangles, but since a triangle is just a particular arrangement of 3 lines it should be easy enough to construct with lineTo:

Triangle Coordinates

When using fill(), open paths are closed automatically, from the current point back to the starting point. That’s why lineTo(150,100) wasn’t necessary at the end. If stroke() is used instead of fill, however, then a call to closePath() is required.

Rather than painstakingly arranging triangles at specific coordinates on the grid, a different approach is to move coordinates via translate and draw the triangle at 0,0. Using this approach to arrange a set of 10 triangles around the center of the canvas:

trianglerad

Now playing with the orientation of the triangles by adding the line:

just before the call to drawTriangle.

Adding a scale function is a lazy way to draw multiple rings

arrows

Combining this exploration into a project:

See the Pen Triangle Spirals by Kentskyo (@kentskyo) on CodePen.0

 

Canvas – Lines, Circles and Spirals

It’s time to branch beyond rectangles for our drawing elements. To draw a line between two points with the canvas api, using the context object:

beginPath starts recording a path. Like Illustrator and some other drawing applications a path is just a set of points it doesn’t become an actual line or shape until a stroke or fill is applied. The canvas can have only one path active at a time.

lineTo(x,y) draws a path from wherever the last point was positioned on the current path to the point directed by x,y in lineTo. This initial point can be set by moveTo(x,y) or it can be wherever lineTo left off in the last call. For example:

We had to start the path with a call to moveTo(x,y) but after that we just piggybacked on wherever lineTo(x,y) left the last end point to begin the next line.
randomlines

Circles

Rectangle is actually the only shape canvas knows how to draw. Everything else must be built from paths. A circle is created using the arc function and telling it the starting angle (in radians) is 0 and the ending is 2*PI:

arc(x,y, radius, start-angle, end-angle, anticlockwise);

2015-11-05_105722

Combining what we’ve covered so far:

circlesandlines

Spirals

To draw a spiral we just need to draw a circle with a radius that either grows or shrinks.

Switching the formula in the line above from:

to:

creates this:
spiral11

Textures

Besides colors, a fillStyle for a stroke or solid fill can also be a pattern or a gradient. Patterns are loaded from images and have an option for how they are tiled across a fill via the repeat parameter:

Combining patterns with spirals was the focus of the next experiment

See the Pen Textured Spirals by Kentskyo (@kentskyo) on CodePen.0

 

 

Canvas – Transforms

The Canvas API has 3 basic transform functions. What’s a little non-intuitive about these functions is they act on the canvas itself, and only indirectly on the canvas contents.

In Canvas – The Beginning the coordinate system was introduced with 0,0 defaulting to the top left corner of the canvas; which was kind of weird because the 2D plots most are first introduced to depict 0,0 in the center of the graph.

Translate

The Translate function moves this 0,0 origin to an offset passed in the x,y argument. So to move the origin to the center of the canvas on the screen:

Transform functions effect what is drawn after the function, it doesn’t effect the contents that already on canvas. A rectangle drawn after the translate function call above:

Results in this

translate1

Rather than this (if drawn before the translate):

Note that the rectangle is drawn from its top left corner. So to center the rectangle in the canvas after the translate, its x and y need to be adjusted:

 

translate2

Rotate

Rotates a canvas around its current origin. That origin part is key to some nifty effects (and confusion sometimes.)

To rotate the rectangle drawn previously 45 degrees:

rotate

the rotate function takes radians as its argument and the formulas for switching between degrees and radians are:

radians = degrees * Math.PI / 180
degrees = radians * 180 / Math.PI

Note this will rotate everything that is drawn after rotate is called until the transformations are reset back to default either through:

ctx.save() and ctx.restore() which saves and restores the state of the canvas, including the state of the transformations, fills, line sizes, etc (but not the graphics on the canvas.)

or

which is faster, but just restores the state of the transformations back to their default settings.

Scale

ctx.scale(x,y) scales pixels (by default) x horizontally and y vertically where x and y are real numbers and 1.0 = current size and < 1.0 shrinks and > 1.0 expands. So 0.5 pixels would be half size and 2.0 would be twice as large.

Negative values can create some interesting effects:

Trig and the Circle

Trigonometry is all about the triangle. What makes it useful for navigating points and angles around a circle is illustrated in this diagram.

trig

Trig functions, like sine and cosine are ratios of different sides of triangles at different angles (in radians) and this ratio can be used to draw an object at a specific point on the perimeter of a circle of any radius.

Another trig function, the tangent, or rather an inverse variant of it Math.arctan2(), when given the length of two sides returns the angle. Very handy for rotating one thing to face another or reading a value off interface elements like dials.

Using a couple of these transforms, and some trigonometry, the next experiment explores aesthetic effects of rotation on a simple construct. Try playing with the rotation speed and blur sliders to see a range of visuals that can be created from a few spokes of rectangles.

See the Pen Pinwheels by Kentskyo (@kentskyo) on CodePen.0

Canvas – Scaling

Adapting a canvas application to display on multiple devices with different resolutions and aspect ratios is a gnarly but necessary task. Before diving further into creating canvas art, this post designs a framework that handles scaling issues and can be found on codepen as a template.

Presenting canvas art on multiple devices usually involves one of two strategies:

  1. Scale the real dimensions of the canvas (may be hard, depending on the design.) The physical dimensions of the canvas change; the position and size of the canvas elements must adapt programmatically based on the dynamic resolution and aspect ratio of the device.
  2. Scale the Canvas element (challenging, depending on the range of devices.) This approach starts with a fixed canvas size; the position and size of canvas elements target this size and the canvas dimensions do no appear to change to the program, however, the canvas element itself is visually scaled used CSS to match the device resolution and aspect ratio.

Approach #1: Code for Resolution Independence

Generally this is much trickier to code for. Starting with a simple example:

on a device with screen dimensions of 1024 x 768 this code will result in this:

2015-09-26_192120

Note the page background color is a lighter red than the canvas background to demarcate what is happening. The canvas can be repositioned with CSS but here it defaults to the standard flow in HTML where elements are written from the top, left corner to the right until they wrap or extend off-screen.

To own the space on the page with a resolution independent approach every element drawn on the canvas needs to adapt:

Now on the 1024 x 768 layout it looks like this:

2015-09-26_195214

It’s elements are skewed because it ignores the aspect ratio of the original (300×300 or 1:1)  It just took whatever ratio was needed to fill the display area using the following for its adaption:

For a best fit, preserving the original aspect ratio but filling as much of the viewport as possible a different approach is required:

Now it stays proportional and fills the max available space with those proportions:

2015-09-26_201556

adding a little tweak to center the canvas on the page:

2015-09-26_202339

Note the CSS background color now brackets each side of the centered canvas. This is called a “letter-box” Some ways of mitigating this padding are discussed a bit later.

Approach #2: Scale the Canvas Element

The second technique, scaling the canvas, uses CSS to resize the canvas element and doesn’t require adapting any of the canvas code or recalculating coordinates. As far as the code is concerned it’s still operating in a 300×300 pixel world:

A CSS Transform could be use in place of modifying the style width and height above, and may prove faster as transforms are often optimized in the GPU of modern devices:

Some notes on managing letter-boxing:

  • Use a background image on the page that blends with the canvas.
    https://css-tricks.com/perfect-full-page-background-image/
  • Extend the canvas a little more than needed, center it and keep all the critical operations with a safe-area
    http://www.williammalone.com/articles/html5-game-scaling/

Choosing a good aspect ratio helps mask the letter-box effect. Unfortunately there is no universal ratio that works best, it all depends on your target audience/devices. Popular ratios for HTML5 game development are:

  • 800×500 (1.6)
  • 960×540 (1.77..)
  • 960/640 (1.5)

Viewports

On desktop browsers it’s pretty simple, the size of the viewport is the size of the browser window. On mobiles it gets squirrelly.

A mobile device, with physical screen of 480 x 640 css pixels, for example, runs into a typical website, not optimized for mobile or responsive, which generally has widths between 800 and 1024. In order to try to display this site without zooming into a corner, the mobile will claim it has a default width of 980 (usually, though it varies by mobile device) This is the layout viewport, used to accommodate sites designed for desktops. A second viewport, the visual viewport is different and defines the area of the layout currently visible. This viewport zooms in and out on the content without affecting the layout viewport. A third viewport, the ideal viewport, is the dimensions that the mobile device reports as optimal for a display area that doesn’t need to zoom. This viewport is defined by the mobile’s browser (i.e. different vendor’s browsers on the same mobile may have different ideal viewports) and usually matches, more or less, the physical CSS pixels (not the device pixels) that the mobile is capable of.

When a meta tag in the HTML states:

it tells the mobile to use the ideal viewport size instead of the default layout size and basically means the site is mobile ready.

This will be used for the canvas html as well, but it needs some additional tweaks:

While heretical to responsive design, where disabling user zoom is regarded as evil, it is essential for our canvas art where a double touch can result in zooming parts of the canvas off the screen.

Those are the hacks to-date, with more experiments this article may undergo further revision.

There is also a set of strategies for optimizing for high pixel densities, where 1 CSS pixel may map to 2 or more actual device pixels. Topic for a future article though.

Some additional reading and sources: