Tuple's Big Refactor (v0.79)

Earlier today, we released version 0.79 of Tuple with massive under-the-hood changes:

diff stat of main PR

In it, we overhauled Tuple’s low-level signaling code (responsible for handling connections between clients).

This has a few user-facing benefits:

  • Snappier UI responses due to better use of threads.
  • Two users calling each other at the same time will now successfully connect.
  • Fewer availability bugs (like not seeing someone online when they’re connected).
  • Fewer crashes.

This also has some great things for us as Tuple’s developers:

  • Lots of new automated tests.
  • Code that’s far easier to reason about.
  • Better decoupling of UX and underlying signaling code.
  • A much better foundation on which to build our next set of features.

If you’d like to hear more of the details, I recorded this walkthrough with engineer Mikey:

We’re excited to have this release live, but even more excited by what it’s going to let us build in the future.

Two other quick items:

  • We have a beta version of a Slack integration! Now you can /tuple @coworker to kick off a call from a DM. Please give it a try and let us know how it can be better.
  • We’re dropping High Sierra support from Tuple on November 1st, when Apple is expected to stop issues security updates for it. You’ll need to update your OS before then if you’d like to keep using Tuple.

If you’re already a Tuple user, you’ll receive this update automatically (or can download the latest version here).

If you haven’t created a Tuple account, you can sign up for a free trial here.

Tuple's August Release & Other News

Quick summary

  • We’re hiring a WebRTC expert to bring mobbing support to Tuple. Please apply or share the job posting with someone great.
  • We added some new features: an (optional) persistent indicator showing which display you’re sharing, we now exclude the webcam window from your screen share, and made a bunch of small improvements to make Tuple more Mac-y.
  • SSO users: you no longer have to sign in after every app restart. Woohoo!
  • We’re dropping High Sierra support in November, when Apple is expected to stop issuing security updates for it.
  • We have some solid improvements in the pipeline: fewer crashes, better automatic call reconnection, better support for 3-person calls, and some onboarding improvements.
  • If you could use someone to pair with, you can get $40 off your first Codementor session by signing up through this link.

A short hello from Ben (and a favor request)

News items

We’re hiring a WebRTC expert (and would love your help)

We want to hire an experienced WebRTC programmer to help bring mobbing (meaning pairing sessions with more than 3 participants) to Tuple.

If this feature is on your wish-list, you can help out quite directly by referring awesome candidates to our job posting.

We’re moving to a monthly release cadence

A number of you have requested that we update the app a bit less frequently. We’re going to test out doing monthly releases for a while to see how it feels.

So far, so good.

We’re dropping High Sierra support on November 1st

November 1st is when Apple is expected to stop offering security updates for High Sierra.

We think it’s important to only run Tuple on secure systems, so we’ll disable the ability to run Tuple on High Sierra near that date.

This change affects less than 2% of you, but we wanted to give you plenty of notice anyway.

We’ve partnered with Codementor so you can pair with experts

Need someone to pair with? Want to get a quick tech question answered by an expert? Codementor is a great way to bring in short-term development help.

As a Tuple customer, following this link will give you $40 in free credits to use on mentoring sessions or freelance jobs. (Any unused credits will expire in 30 days.)

New stuff in Tuple

We pushed v0.78 today. Here’s what’s new.

More Macification

Thanks to our Mac expert Mikey, Tuple has been getting more and more Mac-y. Here are a few small examples:

  • More of Tuple’s sub-windows (like preferences or other models) now respond to standard macOS shortcuts like Command+w and Command+`.
  • Tuple now shows up in the Command+Tab results in a more predictable way.
  • You can now select a call rating by double-clicking the rating emoji or by pressing Command+[a number between 1 and 5] (and then Command+Return to submit).
  • Previously, if you were trying to type on a remote machine but had your mouse outside the Tuple window, we wouldn’t send your keystrokes. That wasn’t very Mac-like (it was more X11-y, really), so we changed it. Now, your keystrokes will be sent whenever the Tuple window has focus (provided you haven’t disabled this in the guest toolbar).

New: display sharing indicator

Lots of folks have told us that they love how unobtrusive the Tuple UI was, but said maybe we’d gone just a touch too far and should have a persistent element that makes it super clear when you’re sharing your screen.

We agreed, and added what we hope is a clear, but still-unobtrusive border:

screenshot with red border

This feature is on by default, but you can disable it in preferences if you prefer Tuple to run in stealth mode.

New: webcam sharing exclusion

If you’re sharing your screen, and someone shares their webcam with you, it creates a weird situation where they’re forced to watch a slightly-delayed version of their own webcam feed on your screen.

That’s distracting, but it’s also bad for CPU and bandwidth usage, since Tuple needs to capture/encode/transmit a live video feed as part of your screen share. Higher resource usage and (likely) more latency. Ew.

To get rid of this unfortunate situation, we now magically exclude the webcam window from your screen share.

In this first screenshot, I’m sharing my screen with Joel, and he’s turned on his webcam. This is what I see on my actual desktop:

screenshot with red border

And here’s what Joel sees: screenshot with red border

Poof! No webcam.

One really cool detail: since your pair might click on the webcam window and not know they’re doing it, we automatically send their clicks through the window so they can interact with elements underneath it as normal. Kind of slick, right?

Bug fixes

  • You might not know this, but there’s a Tuple preference to persist all drawings on the screen until their creator has right-clicked. This used to only work for guests, but now it works for hosts as well. Paint away, friends.
  • SSO folks: you should no longer have to sign in after every app restart. Sorry you had to live with this for so long.
  • When you’re using the Tag Team mouse mode, there is just one mouse cursor, and each person takes turns using it. To get control when you don’t have it, you click one time. For guests, we’ve always swallowed this first click so you don’t accidentally click something when you’re just trying to get control. Now, we do this on the host side too.
  • If you’ve enabled the preference to automatically start a webcam feed when calls begin, this will be respected for audio-only calls too.

Upcoming improvements

Here’s a sneak-peek at what we’re shipping soonish:

  • Fixes for our most common crashes.
  • Far fewer dropped calls.
  • A fix for the issue where two people calling each other at the same time can lead to unpredictable behavior.
  • A better first-run experience for the app.
  • Better support for 3-person calls (observer mode). Currently, observers can’t draw on the screen, and the presence of an observer adds surprising limitations on the other call participants. We’re going to do a pass on this feature to flesh it out and make it more useful.

Until next month!

Redesigning the Tuple Client UI

After two years of building Tuple, the time has come to redesign our Mac client. We’re very excited to solve many usability problems that have cropped up over time and for our aesthetics to reflect the quality of the Tuple internals.

This post is a deep dive into how we planned, executed, and shipped the new design.

Shaping it up

Since this was a rewrite of the front-end codebase, we planned diligently to ensure scope did not creep too far. First, we took a page out of the Shape Up methodology and kicked things off with a pitch by our CEO, Ben.

Here’s an abridged version of the pitch that Ben wrote up in a Notion document:

### Problems

The Tuple client (the installable Mac application that lives in the menu bar) 
has sub-par UI/UX.

The following issues are paramount:

- People don't realize they can add an observer to a call 
- People don't realize they can swap roles easily
- The changelog is hidden
- The "End" button is unclear
- Call controls are not clear to all users
- Inviting new users is hidden away and confusing
- Team directory is maybe not necessary
- Online status not clear

### Appetite

Let's keep this project to 4 weeks.

After reviewing the write-up, Ben and I hopped on a quick call to talk it through and fill in the gaps. My goal was to learn what items were most pressing and risky, in case we had to cut scope to stay within the 4-week budget.

Next, I jumped on a Tuple session with Spencer to talk through the technical details. I learned the interface is a React app that gets rendered in a Safari WebView and communicates with the Swift app through a JavaScript bridge. The Swift app is in charge of managing state, passing that state to the WebView, and listening for various commands issued from the React app.

Fortunately, the boundary between the React and Swift apps is quite clean, so we decided to build the new front-end in a separate codebase and compile it into the native codebase during the build phase.

Setting the foundation

The original codebase was clean and well-written, but it was beginning to show its age. It was mostly written in the pre-hooks era, so all the components were implemented with classes. CSS styles were embedded directly in the JavaScript, but we knew we wanted to utilize Tailwind CSS & Tailwind UI moving forward. The changes we wanted to make were significant enough that I decided to build new components from scratch instead of adapting the existing ones.

I chose to start with the main home screen to develop the new visual identity:

Old Main Screen

After sketching a bit with pen and paper, I cracked open my editor and created a new project. I prefer to prototype with HTML/CSS as early as possible, so I skipped the typical phase of prototyping in Sketch or Figma. Tailwind enabled me to style rapidly with utility classes.

Here’s how the main screen shaped up:

New Main Screen

Connecting with people

Connecting with teammates and friends on Tuple used to be quite confusing. In the old interface, users had to click the gear icon to open a dropdown menu, where they could either choose to “Invite Teammate” or “Add Friend”:

Old Invite Flow

These two options sound confusingly similar, and customers often picked the wrong one. As someone quite familiar with the app, even I had to study the data model for a while to grasp the nuance.

Ultimately, we decided it was best to unify these two forms under one view. Instead of forcing the user to choose upfront, we now funnel everything related to connecting with people through one interface: the “Connect with people” form.

New Invite Flow

Smoothing out the in-call experience

The team identified a handful of UX issues with the in-call view:

Old Call View

Here’s how the new in-call view turned out:

New Call View

Sharing progress with the team

This interface has a lot of different possible states – by the end of the redesign, we were handling over 25 distinct permutations. Fortunately, it was easy to simulate each state by passing mock data to the React app.

Our <App> component has two main props: a state object representing the state of the application, and a cmd function that dispatches commands back to the Swift application. Each time I designed something to handle a different permutation, I added a new instance to our demos page backed by a new state object in our /fixtures directory. Gradually, the page grew to represent every possible unique view users might see.


I also implemented a mock cmd function that would simulate changes to the state object, so the team could click around the app and get a feel for how it would behave in real life. For example, clicking the “Call” button would put the app into an “outbound calling” state and then, moments later, flip to the “in call” state to simulate someone answering the call.


After about a month of development and iteration, Spencer plugged the new interface into the Swift application and pushed it live behind a feature flag. We chose a few of teams to beta test and give us feedback. This process unearthed a handful of small bugs and points of confusion, once again proving it’s impossible to anticipate every scenario that could use improvement.

We hope you enjoy the facelift and find Tuple app even easier to use!