Image for post
Image for post

Bringing the Westworld Attribute Matrix to Life

Notes on diagonal slider design and data storytelling

Like many data viz and UI nerds out there, I was smitten with the interesting concepts presented in Westworld. I was especially keen on Maeve’s attribute matrix—a radar chart that plots her personality attributes on a 20-point scale—and what it might feel like to program a host by simply dragging the vertices of the star. With a couple weeks to myself, I tried coding an interactive tribute to the Westworld chart using JavaScript.

You can see the finished project here and on GitHub.

Image for post
Image for post
Maeve’s attribute matrix from “The Adversary,” Westworld Season 1

Though there are pre-made radar charts I could have started from, the fun of this project would be in figuring out the math for myself. There were a few things I hoped to get out of this exercise:

  • Experience building an interactive data visualization. I had integrated third party charts before, but I’d never coded one from scratch.
  • A chance to practice some trigonometry. I ended up using a few trig functions to calculate points, and the Pythagorean theorem to measure distances.
  • A renewed perspective on the effort behind a complex UI. As someone who does more design than development, it’s easy to forget how hard it is to make even the most narrowly scoped things. This project succeeded in being a humbling reminder.

In the end, the project was a great technical and design exercise, offering some interesting math problems and an opportunity to rediscover an important aspect of effective data visualization.

Making a Diagonal Slider

There were a lot of layers that went into building the chart, but the hardest part was creating a diagonal slider. From a design perspective it might seem like a trivial detail, but starting with the slider proved to be a good foundational move. The math was a little less straightforward than I first thought and there were a few conditions I hadn’t anticipated. But once I got the slider working, building out the chart was mostly a matter of coordinating multiple instances of a now robust component.

To start, I took stock of the data I had available:

  • (x, y) coordinates from a touchmove or mousemove event, generated whenever the user drags their finger or pointer over the chart area.
  • An arbitrary slider angle θ.
Image for post
Image for post

From here I derived a pair of coordinates (x’, y’) that would position the slider along the slope of the angle. Using the tangent of θ to match the slope, there are actually two possible coordinate sets: (A) pointer x and slider y’ or (B) slider x’ and pointer y. I wasn’t sure which would feel right until I programmed it, so I decided to just build (A) and test it out.

Image for post
Image for post
It wasn’t clear which method would work better, so I just tried Method A first.
// Method Aslider.x = pointer.x;
slider.y = pointer.x * Math.tan(Math.radians(angle));

Method A seemed to work so long as the slider angle was near the x-axis. In the screen capture, you can see the slider tracking nicely at 25°. But when I tested near the y-axis at 75°, the slider didn’t follow the pointer properly.

Image for post
Image for post
Image for post
Image for post
Method A: Using pointer x and deriving slider y’ only worked well for angles near the x-axis.

On a whim, I plugged in the code for Method B to see if it would fix the 75° issue—and it did! I ended up needing both Methods A and B, depending on the slider angle’s proximity to a horizontal or vertical axis. Sliders within 45° of the x-axis were corrected with a horizontal bias (Method A), and sliders within 45° of the y-axis were corrected with a vertical bias (Method B).

Image for post
Image for post
let closerToVerticalAxis =
(angle > 45 && angle <= 135) ||
(angle > 215 && angle <= 315);
// Method B: Vertical bias. Use given Y, solve for X.
if (closerToVerticalAxis) {
slider.x = pointer.y / Math.tan(Math.radians(angle));
slider.y = pointer.y;
// Method A: Horizontal bias. Use given X, solve for Y.
} else {
slider.x = pointer.x;
slider.y = pointer.x * Math.tan(Math.radians(angle));
}

Setting Slider Limits

With the basic slider drag interaction out of the way, I moved on to the next challenge: ensuring the slider didn’t drag outside the chart.

The first step was in understanding what defined the length of the slider. Knowing the slider would eventually be part of a radar chart, I assumed the chart radius would set the length.

// Determine radius based on chart proportions.if (chart.width > chart.height) {
radius = chart.height / 2;
} else {
radius = chart.width / 2;
}

With the coordinates of the slider position, I could use the Pythagorean theorem to figure out how far the slider has traveled. And to prevent the slider from going beyond the radius, I positioned the slider at its terminal location whenever the drag distance exceeded the radius.

Image for post
Image for post
// Determine drag distance using Pythagorean theorem.let dragDistance = slider => Math.sqrt(slider.x**2 + slider.y**2);// If drag distance exceeds chart radius,
// set slider to terminal position.
if (dragDistance(slider) > radius) {
slider.x = radius * Math.cos(Math.radians(angle));
slider.y = radius * Math.sin(Math.radians(angle));
}
Image for post
Image for post
The working slider!

Finishing the Chart

I can’t trivialize the amount of work that went into making the rest of the chart, but things did move relatively quickly after I got the slider working. Highlights:

  • Making the slider responsive. The slider terminal and position scale proportionally to the chart radius.
  • Creating the attribute data structure. The entire chart is programmatically drawn based on data from a JSON host file.
  • Drawing the chart. I used Snap.svg to draw the rings for the 20-point scale, slider tracks, and star.
  • Adding the attribute labels. Aligning the labels was tricky at first, but I was able to leverage the code that positioned the slider terminals.
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post

Telling Maeve’s Story

Even after I got all the details working as I had originally hoped, something was missing from the chart. I felt the piece could be more compelling if it stretched beyond the original design to show a more personal perspective.

At first I thought I could make it more interesting through visual design: some Westworld interface motifs around the chart; maybe some animation.

Image for post
Image for post
An early attempt to expand on the original chart.

I was really fond of the punchcard button geometry, especially in how it contrasted with the symmetry of the radar chart. And I loved the possibility of the star reshaping itself to match a selected host. But the interaction design was too contrived. While it would look great in motion, it would be dishonest to the data structure behind the chart: not all hosts will use the same attributes, so it’d be more realistic to load an entirely different chart rather than animate the existing sliders.

To create an honest animation between alternate charts, I’d have to find natural shifts in data for just one host.

Maeve underwent a major transformation on the show, reprogrammed several times by technicians and even Maeve herself. With at least five distinct configurations throughout Season 1, Maeve’s evolution was the perfect framework for creating a dynamic radar chart:

  • [1.0] Homesteader
  • [2.0] Madam of The Mariposa
  • [2.1] Narrative team’s modifications: increased aggression
  • [2.2] Elsie’s corrections: lowered aggression, increased perception and emotional acuity
  • [3.0] Maeve’s self-modifications: lowered loyalty, decreased pain sensitivity, increased bulk apperception

I took some liberty with the chart adding Perception and Emotional Acuity as attributes. They’re not actually part of this attribute group in the show, but including them made Elsie’s modifications more noticeable. (And with our JSON-powered chart, it was easy to do: just add the attributes to the host profile.)

Image for post
Image for post
The completed chart tells Maeve’s story as a series of host configurations. Try it yourself.

The host configuration nav added the missing bit I was looking for: a sense of time. Time helped the visualization start to tell a story and inspire questions about Maeve’s past and future. Among Westworld fans in my family, the new chart succeeded in sparking conversation about what Maeve was truly like as a homesteader, and what modifications might be in store for an increasingly sentient machine.

Image for post
Image for post
Maeve’s updated (and still evolving) JSON file.

Next Steps

With more time, here are some things I’d consider exploring further.

Visualization:

  • Take the time to accurately model Maeve at each configuration. Try to define each point in the scale for each attribute.
  • Rearrange the attributes around the chart to create better affinities and more meaningful star shapes.

Interaction:

  • Make the chart more touch friendly. It’s hard read value changes for sliders near the 6:00 position because your finger covers the attribute label.
  • Make the chart small-screen friendly. Consider keeping the star shape as a holistic read of Maeve, but use conventional sliders to control the attributes.

Emilio Passi is an interaction designer and technologist in San Francisco, California.

Written by

Principal Design Technologist @ Punchcut

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store