Che Wei (Jacky) Lee
Wahoo Product Design
7 min readSep 28, 2022


Blurring the Lines Between Prototype and Real Product

Designing with data is becoming the new norm. Excel sheets, APIs, data randomisers are all readily supported in our tools to test our designs with realistic scenarios. At Wahoo Fitness, the types of data we try to visualise are generated from workout activities. A workout consists of timestamps of multiple data streams such as gps, heart rate, power, cadence, speed and many more. It’s one thing to design for visualising a completed workout activity, but it’s a different set of challenges designing for a workout that’s in progress. The experience we design involves the fluctuation of efforts, heart rate, perspiration and mood during a workout.

How do we design for real time experiences? How do we set up our design tools so our biometrics can directly manipulate design components, while we’re designing?

Knowing the end experience will involve data visualizations that animates according to the user’s biometric input, low fidelity static wireframes will struggle to accurately convey this design intent. I could use keyframe animation tools like Adobe After Effects to compose detailed animations for each interaction, but the animations would be choreographed and wouldn’t be accurate when testing.

For this prototype to simulate how the finished product will work, it needs to have the same bluetooth pairing capabilities. The goal was producing something that will look and behave as similar as possible to the actual product that will ship. With these goals in mind, I followed this tutorial and learnt how to use Chrome’s Web Bluetooth API to print my heart rate value into the console. I then replicated the same bluetooth functionality inside a code component in Framer. I have seen examples that used Code Overrides to replace text label values with data, I figured Injecting data from bluetooth should work the same way, so began hacking away at it.

The result was a hi-fidelity Framer prototype that streams data in real time from bluetooth enabled heart rate monitors and indoor smart trainers. The sensors used (not limited to) were Wahoo TICKR Heart Rate Strap & Wahoo KICKR Smart Indoor Trainer.

How it connects

To funnel my heart rate data into any design components in Framer, I used a global state manager library called ReactN. It gives every component the ability to sync with a shared data stream across the entire prototype. The way to do this with ReactN is using a useGlobal() hook declaration on the top of each component. Before we dive into the examples, the way data is passed around is this:

  1. A BluetoothHeartRate code component handles pairing with bluetooth heart rate monitors. For security reasons, it's required to trigger this pairing process with a user gesture like a tap. A dialog will open in Chrome browser where you can select a heart rate hardware from the list and tap 'Pair'.
  2. If pairing was successful, every time BluetoothHeartRate receives a new heart rate value, we will save that value into a global state manager (ReactN) as an array. Simply put, an array in javascript is a single variable that stores a list of elements. In this case, the array I am storing is a list of my heart rate values, that looks like this: [60, 64, 65, 65, 68, ...]. The gif below is a demonstration of printing out the heart rate array into the console every time this component receives a new heart rate value.

3. Once we have the heart rate array actively saving in the global state manager, any component can use the useGlobal() hook to retrieve the same stream of heart rate array. Each component can render the value however it wants. Below is an example of every component using useGlobal() hook at the top level.

1. Heart rate values (Current & Peak)

After retrieving the entire heart rate array from our global state manager, we can perform functions on the array to get useful values we need. Here are two component examples that displays different things from the same array.

  • CurrentHeartRate — displays the user’s current heart rate.
  • PeakHeartRate — displays the user’s highest heart rate recorded since the bluetooth connection was paired successfully.

I have also set up additional Framer-specific property controls to customization them for light & dark themes.

Current heart rate is the last item in the heart rate array. To retrieve this, we can use a slice function. Check out the code behind CurrentHeartRate component.

// Find the last item in the array
let currentHeartRate = heartRateArray.slice(-1)[0]
// Render the last item in JSX
return (

Peak heart rate is the highest value from of the entire array. To retrieve this, we can use the Math.max() function. Check out the code behind CurrentHeartRate component.

// Find the highest value in the array
let peakHeartRate = Math.max(...heartRateArray)
// Render the highest value in JSX
return (
<span>bpm (peak)</span>

3. Heart rate line chart

To plot the entire array with a chart, I used react-chart-js2 library to achieve this. Every charting library may require you to format the data differently, but react-chart-js2 can take an array of numbers and turn it into a line chart. This was exactly what I needed. I added Framer property controls on top of this component to configure the color, fill, line width and axis of the charts. Check out the code behind this component.

4. Heart rate zones

Knowing what heart rate zones you are currently training in is useful. Every athlete has slightly different zones because their maximum heart rate is different. For ZoneHeartRate component, I used the current heart rate value to determine the zone it belongs in, then animate a shape’s size and color accordingly. I built additional property controls so I can tweak the maximum heart rate and customise the color for each zone on the fly. Check out the code behind this component.

At the end of this exercise, I created 6 heart rate related components. All of them serving as building blocks for larger compositions. With Framer’s property controls I could quickly iterate various compositions and color schemes on the fly. It was great knowing that the data will always be in sync between every component. This was also the first prototype that forced me to do jumping jacks and squats in order to fluctuate my heart rate so I can test the animations.

In addition to heart rate data, I replicated the exact same components to stream power data from my indoor smart trainer. This meant I could let my bike-riding efforts dictate the animation of the interfaces.

The end result was a hi-fidelity working prototype that behaved like the finished product. We could mimic a live indoor cycling workout by injecting live data from Wahoo TICKR Heart Rate Strap & Wahoo KICKR Smart Indoor Trainer. I had the privilege to demo this in front of a live audience in 2019 Framer Loupe.

What’s next

Building this prototype has unlocked great potentials for what I am currently working on at Wahoo Fitness. Our mission at Wahoo has always been about helping athletes train smarter, with data. Having tools that blur the line between prototype and the end product gives us a unique way to express, test and iterate ideas without writing any production quality code; The closer we can get to testing prototypes that looks & feels like the end product, the faster we can validate and know if an idea has legs.

An interesting part of this exercise was the data-first approach — understanding the type and structure of data I could get from the heart rate strap gave me better ideas for how to visualize them. As designing with data become the new norm, I am excited about the future of code-based design tools and would love to see a day when data binding our interfaces is as simple as picking a typeface or color.

In terms of what’s immediately next… I think I’m going to build a simulator so I’m not required to physically do jumping jacks just so I can fluctuate my heart rate and test my designs.



Che Wei (Jacky) Lee
Wahoo Product Design – Product Designer and Developer made in Kaohsiung, Taiwan, raised in Auckland, New Zealand, now based in New York, USA.