Run for your money: Engineering a treadmill that prints store credit

We combined FTMS sensors, Socket.IO, and POS UI extensions to turn a manual treadmill into a discount-printing machine.

Nikola Draca
9 min readbeginner
--
View Original

Overview

Shopify partnered with Endorphins Running to build a gamified treadmill experience for the 2025 NYC Marathon weekend that rewarded runners with in-store credit based on pace consistency. The system combined Bluetooth FTMS hardware sensors, a Python treadmill server, a Remix web application with Socket.IO for real-time communication, and a custom Shopify POS UI extension for seamless discount redemption at checkout.

What You'll Learn

1

How to connect Bluetooth FTMS fitness hardware to a Python server for real-time data processing

2

How to build a real-time gamified experience using Socket.IO, Remix, and WebSocket event-driven architecture

3

How to build a Shopify POS UI extension that scans barcodes and applies discounts automatically

4

How to normalize irregular sensor data intervals for consistent pace-to-credit calculations

5

Why decoupling the treadmill experience from the checkout system improves reliability in live retail events

Prerequisites & Requirements

  • Understanding of WebSocket and real-time event-driven communication patterns
  • Familiarity with React and component-based UI architecture
  • Experience with Remix framework or similar full-stack React frameworks(optional)
  • Basic understanding of Bluetooth protocols and hardware sensor integration(optional)
  • Familiarity with Shopify POS and its extension APIs(optional)

Key Questions Answered

How do you read real-time speed data from a treadmill using Bluetooth FTMS?
A Python script pairs with the treadmill via Bluetooth and listens for FTMS (Fitness Machine Service) messages, a common protocol for smart fitness equipment. The script receives speed data from reflective sensor strips or built-in treadmill displays and relays events to a Socket.IO server. This works with both external sensors like the Runn sensor and built-in treadmill displays that support FTMS.
Why use a manual treadmill instead of a motorized one for a pace-based game?
Motorized treadmills automatically maintain a set speed, making the data uniform regardless of runner effort. A curved manual treadmill like the TrueForm Trainer has no motor and is entirely powered by the runner, making it immediately responsive to pace changes. This produces variable speed data that accurately reflects the runner's actual performance, which is essential for a game that rewards pace consistency.
How do you normalize irregular Bluetooth FTMS sensor intervals for consistent calculations?
FTMS messages from treadmills are not sent on a fixed schedule. The solution is to compare the timestamp of the last interval to the newest one and calculate the difference as a percentage of a second. This normalized interval is then used as a multiplier for the credit-per-second rate, ensuring accurate credit accumulation regardless of how frequently the sensor reports data.
How does the pace zone scoring system work for converting running speed to store credit?
A target pace is defined with padding on either side to create a 'zone.' For every second the runner stays within this zone, they accumulate in-store credit proportional to the time spent. In this implementation, runners earned 1/30th of $75 for each second in the zone during a 30-second session. The system rewards consistency over raw speed, creating an even playing field for all fitness levels.
How do you build a Shopify POS UI extension that scans barcodes and applies discounts?
A custom POS UI extension is created with a 'Pay with Pace' tile that launches a barcode scanner. When a barcode is detected, the payload includes a user ID which is used to fetch the user's email and discount code from the database. The extension then programmatically adds the customer to the cart and applies their unique discount code, while also creating or updating the customer record in Shopify for future engagement.
How do you print custom receipts with barcodes from a Remix web application?
A custom Receipt component takes finished user data and renders a receipt object using the Epson ePOS JavaScript SDK. A usePrinter custom hook wraps the printer object in React Context, making it accessible from anywhere in the Remix app without re-instantiation. The receipt encodes the user ID in a barcode that can be scanned by the POS UI extension at checkout. The same pattern works with most receipt printers or can be replaced with digital QR codes.
Why should you decouple the in-store experience from the checkout system?
The POS UI extension was intentionally hosted on a live production server separate from the local MacBook Pro powering the treadmill. This decoupling ensures that if the treadmill system needs debugging, checkout is not blocked. In a live retail event, any hardware or software issue with the experience should never prevent customers from completing purchases with their earned discounts.
How do you manage state across multiple displays and control panels in a real-time event?
A state machine manages game flow with states like warm-up, running, and session complete. The control panel sends WebSocket events with a state_update type that all connected clients receive and respond to. The Remix app's parent route manages all socket events and passes data to child routes through Outlet context, keeping displays, the control panel, and the treadmill runner's iPad all synchronized in real time.

Key Statistics & Figures

New customer acquisition rate
63%
63% of all treadmill participants were new customers for Endorphins Running
Endorphins Running global community size
25,000 members
Global membership of the Endorphins Running community
Treadmill session duration
30 seconds
Participants had 30 seconds to maintain their selected pace
Maximum credit per session
$75
Runners earned 1/30th of $75 for every second spent in the pace zone
Display count
3 x 4K TVs
Three 4K TVs showing real-time treadmill data for the audience

Technologies & Tools

Some links below are affiliate links. We may earn a commission if you make a purchase.

Backend
Python
Treadmill server script that listens to Bluetooth FTMS events and relays data
Frontend/Backend
Remix
Full-stack web application powering displays, control panel, and game logic
Real-time Communication
Socket.io
WebSocket-based event system connecting treadmill, displays, and control panel
Backend
Express
Custom server for the Remix app with WebSocket handler integration
Hardware Protocol
Bluetooth Ftms
Fitness Machine Service protocol for receiving treadmill speed data
Point-of-sale
Shopify Pos
In-store checkout system with custom UI extension for discount redemption
Extension Framework
Shopify Pos UI Extensions
Custom barcode scanning and automatic discount application at checkout
API
Shopify Admin Graphql API
Creating discount codes and customer records with metafields
Hardware SDK
Epson Epos Javascript SDK
Printing custom receipts with barcodes from the Remix application
Frontend
React
UI components including Receipt component and custom hooks like usePrinter
Hardware
Runn Sensor
Initial prototype Bluetooth FTMS sensor using reflective 3M strips
Hardware
Trueform Trainer
Curved manual treadmill providing responsive pace-based speed data
Development Tool
Cursor
AI-assisted development for implementing interfaces between systems

Key Actionable Insights

1
Use manual treadmills instead of motorized ones when building pace-tracking experiences, because motorized treadmills produce uniform data regardless of runner effort. Manual treadmills respond immediately to pace changes, providing genuine variability that makes gamification meaningful.
This applies to any hardware-based gamification project where you need authentic user input rather than automated system output.
2
Normalize irregular sensor intervals by calculating timestamps as a percentage of a second rather than assuming fixed-rate data delivery. FTMS messages and many other hardware protocols do not guarantee consistent timing between events, which can lead to inaccurate calculations if you assume fixed intervals.
This pattern is essential for any application consuming real-time hardware sensor data, from fitness equipment to IoT devices, where message frequency varies.
3
Decouple your experiential/interactive system from your transactional checkout system by hosting them on separate infrastructure. This ensures that debugging or failures in the interactive experience never block customers from completing purchases.
Critical for live retail events and pop-ups where downtime directly impacts revenue and customer satisfaction.
4
Use Socket.IO with a local network architecture (all services on one machine) for ultra-low-latency real-time experiences. Running the Python treadmill server, Remix web app, and display clients on the same MacBook Pro eliminates network latency entirely.
Ideal for in-store or event-based installations where a single machine can handle the load and you need sub-second responsiveness.
5
Wrap hardware interfaces like receipt printers in React Context via custom hooks so they can be accessed from any component without re-instantiation. The usePrinter hook pattern makes printer status and functionality available application-wide while maintaining a single connection.
Applicable to any web application that interfaces with physical hardware peripherals, especially in retail or kiosk environments.
6
Store customer metadata from experiential events as Shopify customer metafields to enable future segmentation and personalized marketing. This turns a one-time interactive event into a long-term customer data asset.
Useful for brands running pop-up events or in-store activations who want to build ongoing relationships with participants.

Common Pitfalls

1
Using a motorized treadmill for pace-tracking gamification produces uniform data that doesn't reflect actual runner effort. The motor maintains a constant speed regardless of what the runner is doing, making it impossible to meaningfully gamify pace consistency.
The team discovered this during prototyping and switched to a curved manual treadmill that is entirely runner-powered and immediately responsive to pace changes.
2
Assuming Bluetooth FTMS sensor messages arrive at fixed intervals leads to inaccurate calculations. FTMS data is not sent on a predictable schedule, so naively counting messages per second will produce inconsistent credit accumulation or scoring.
The solution is to normalize intervals by comparing timestamps between consecutive messages and calculating the time delta as a fraction of a second.
3
Tightly coupling the interactive experience system with the checkout/payment system means any hardware or software issue with the experience blocks customers from redeeming their earned rewards at the register.
Hosting the POS UI extension on a separate production server ensures checkout continues working independently even if the treadmill system needs debugging.
4
Re-instantiating hardware connections (like receipt printers) in multiple components causes connection conflicts and unreliable behavior. Each new instantiation may fight for the same hardware resource.
Wrapping the printer in React Context via a custom hook ensures a single persistent connection shared across the entire application.

Related Concepts

Bluetooth Ftms Protocol
Websocket Real-time Communication
Socket.io Event-driven Architecture
State Machine Design Patterns
Shopify Pos UI Extensions
Remix Framework With Custom Express Server
React Context For Hardware Abstraction
Gamification In Retail Experiences
Receipt Printer Integration
Customer Metafields And Segmentation
Iot Sensor Data Normalization
Manual Treadmill Biomechanics