Little Things

from Red Blob Games
21 Feb 2019

Here I have an unorganized collection of "little things" about my interactive diagram design. They aren't necessarily better but they show some of the elements I use. I am using this as a reference for myself.

 1  Visuals#

 1.1 Color in diagrams match text#

I sometimes use colors in the text to match the color in the diagrams. Here's an example from the hexagon guide.

Colors represent concepts in the diagramThe colors represent the same concepts in the code
Semantic colorization: use the same colors for code as used in the diagram

Unfortunately because the text is x, y, z, the colors are not easy to see. I think the colors on the pathfinding pages worked better.

 1.2 Color in diagrams matches code, text#

In this diagram on my breadth first search page, I use a red blob for a pathfinding node, blue for the set of nodes to be explored, green for the neighboring nodes, yellow for the current node, brown for visited nodes. In the code, instead of using standard colorizations for strings, keywords, etc., I use the same colors that I used in the diagram. I use the same colors in the text.

Colors represent concepts in the diagramThe colors represent the same concepts in the text
Semantic colorization: use the same colors for text as used in the diagram

I think I used too many colors though; it would have been better to break this up into several steps, each focusing on one concept.

Related: Evan Brooks's article about semantic coloring of code[1].

 1.3 TODO inline icons in text#

Example of inline legendsAnother example of inline legends
Inline legends: describe elements of the diagram in the text

 1.4 TODO inline icons in text, code#

Diagram has iconsCode can have same icons
Code can use icons to match the diagrams[2]

Charts often have a legend that describes what each of the colors and patterns means. I'm using these as an inline legend that's part of the text.


Also see: sparklines[3]

 1.5 Negative space#

Negative space[4] is the space around the thing you draw. We can use this to our advantage, creating shapes in the mind without actually drawing them. A common example of this for page design is avoid drawing borders. You can often get the same effect by using background colors and leaving some space:

Sidebar sidebar sidebar

I guess I'm a little weird. I like to talk to trees and animals. That's okay though; I have more fun than most people. Let's do that again. There we are. Let's get wild today.

You can do anything here - the only pre-requisite is that it makes you happy. The light is your friend. Preserve it. Maybe there was an old trapper that lived out here and maybe one day he went to check his beaver traps, and maybe he fell into the river and drowned. We don't have to be concerned about it. We just have to let it fall where it will. — Bob Ross text generator[5]

Sidebar with border vs negative space

The first style focuses on the border; the second style focuses on the content.

We can use the same idea for diagrams. It's especially noticeable with grids:

Grid with border vs negative space

Again, the first style emphasizes the edges (which is what I want on a page about edges); the second style emphasizes the tiles (which is what I want most of the time).

We can also apply this idea to node-and-edge graphs:

Circles with border vs negative space

The first one emphasizes the edges and the the second one emphasizes the nodes. Which works better depends on which I want the reader to focus on.

 1.6 Yellow light and blue shadows#

I had read somewhere that artists use yellowish tints for sunlight and bluish tints for shadows. I used this idea for my 2D visibility page:

Blue light with yellow shadowYellow light with blue shadow
Yellow light and blue shadows look better

I don't know a lot about this though, and haven't used this idea much. Also see: The Muller Formula[6], which says lighter colors should be hue shifted towards yellow and darker colors should be hue shifted towards violet.

 1.7 TODO box shadow on draggable numbers

 2  Interactivity#

 2.1 Interaction feedback#

Highlighting options

These effects and others can be combined. Also the drag and hover effects can be different. For example, set the mouse pointer and fill color on hover, and then add a drop shadow and slight rotation on drag. Try dragging this shape:

Example: drag this shape

 2.2 Drag point#

The simplest way to deal with dragging is to set the object's position to the mouse position. This works fine for small shapes but it has a problem that's noticeable with larger shapes. Suppose we pick up the square from its corner and drag to the right. What should happen? If setting the square's position to the mouse position, it will "snap" down and to the right. Instead, we should remember where on the square we're dragging from, and postition the square relative to that.

Moving a shape to the mouse pointer center vs offset

Try it yourself:

Example: try dragging the square from its corner

What does the code look like?

  1. When starting a drag operation, remember the offset between the object position and the mouse position. offset = {dx: pos.x - event.x, dy: pos.y - event.y};
  2. When dragging, add the offset to the mouse position to get the object's position. pos.x = event.x + offset.dx; pos.y = event.y + offset.dy;

There are other equivalent ways to implement this, and which way is easiest will depend on the API and what else you're doing with the mouse coordinates. If you're using d3 — d3-drag[7] supports this feature when you set a "subject" for the drag operation.

A different approach is each time mousemove is called, increment the object's position by the change in mouse position. In some browsers, mousemoveX, mousemoveY let you get that change, and they continue to work even if the mouse is taken off screen (using Pointer Lock). I prefer using the absolute change since the drag started instead of relative position since the last mousemove. It allows constrained movement as I explore on this page, and as you can see in the demo above.

 2.3 Expanded hit area#

The previous section was about dragging something large. We have a different problem when dragging something small. With some input devices, it's hard to be precise enough to point to a small object. Even if you are able to point to it, the act of clicking will move your pointer slightly for some types of input, and that causes you to miss. Even when you do grab it, the mouse pointer or your finger can hide the object.

Example: try dragging the circles

On the left is the regular object drag approach. On the right:

  1. An invisible draggable area is underneath the circle, to make it easier to pick up the cricle.
  2. The cursor goes away while dragging so that you can see the object being dragged. (Alternatively, use the crosshaircursor[8].)

How do I implement this with SVG? I usually make the invisible area draggable, and it updates an underlying position object. I then draw a non-draggable object on top of it at that same position. The next problem is that the non-draggable object eats up the mouse events before the invisible area receives them. Solution:

.draggable { cursor: grab; pointer-events: all; }
.object { pointer-events: none; }

Now SVG will not pass pointer events to the visible object on top, and only pass them to the invisible object underneath. The pointer-events[9] CSS property is quite useful here!

When using Canvas or WebGL, I can't use invisible SVG objects so I have to reimplement all of this. Either go through all your objects and find something close enough to the mouse pointer, or use Voronoi to build to build a map of which object is closest the mouse pointer. The Voronoi approach is used in the D3 community[10], in part because D3 comes with a nice Voronoi library.

 2.4 Transitions#

The main reason I would use a transition is because I want to show the states in between two states you are looking at. Most of the time, the in-between states are automatically visible as you move a slider or other control between one state and another. If I can redraw the diagram quickly enough, I don't need to add transitions or animations in those situations. Transitions are useful when switching between states where the slider/control doesn't go through the intermediate states. An example is the two orientations of hexagonal grids, "flat topped" and "pointy topped". Compare without and with transitions:

Without and with transitions

On the left, you can see the two orientations but it's hard to see that they're related. The transition on the right diagram shows the connection between the two grid types.

I think the default transitions in CSS and d3 are pretty good, but for the hexagon page I added a slight overshoot, like easeInOutBack[11] but subtle.

 2.5 TODO touch events handled separately

 2.6 TODO bump on hex animation

 2.7 TODO controls and labels below (for touch screens)

 2.8 TODO svg outside boundary[12]

I have several examples but will make a new one here

Note that some browsers won't catch events outside the boundary.

 2.9 TODO detect bad input (visibility, circular obstacle)

 2.10 TODO[13]

 3  Canvas + SVG overlaid#


 4  General design#

 4.1 TODO Color & size

Use brighter (saturated) colors for important things. Small areas only. If it's large then it makes everything unimportant. Contrast.

 4.2 HSL colors#

When picking colors, the default is to use RGB. It's well known and widely supported. Web browsers also support HSL colors. I find these much easier to work with. The standard colors are easy to switch to HSL:

RGB vs HSL standard colors

Setting saturation to half (S=50%) results in colors I like better:

RGB vs HSL half saturation colors

With RGB, we replaced 0→64, 255→191. With HSL we had to change S from 100% to 50%. HSL's advantage shows up when you want to go beyond these. For example, changing hue:

RGB vs HSL change hue

or saturation:

RGB vs HSL change saturation

or lightness:

RGB vs HSL change lightness

I find it much easier to work with HSL than RGB. I can pick sets of colors that match each other. HSL is supported for SVG, HTML, and Canvas vector drawing, but not WebGL or Canvas pixel manipulation.

 4.3 TODO text shadow on header text

 4.4 TODO typeface, size

 4.5 TODO hanging-punctuation: first last

 4.6 TODO margins

 4.7 TODO one/two column

 4.8 TODO Site

 5  Site#

 5.1 TODO backwards compatibility - keep things up, it takes time to build traffic

 5.2 TODO url redirects when necessary

 5.3 TODO links to sections

 5.4 TODO minimize build step so that there's no barrier to me fixing something

 5.5 TODO x pages vs regular pages

 5.6 TODO org, markdown, bxml

 5.7 TODO prerender for hex page

Email me at , or tweet to @redblobgames, or post a public comment: