Helping with Hacktoberfest

When we set out to build Tuple, we recognized that we could only do it with such a small team because of the generous OSS developers that had paved the way ahead of us. Fantastic tools like WebRTC and Rails likely saved us years of development time.

We’ve been inspired by Digital Ocean’s Hacktoberfest, and we’d like to join them in giving back to the OSS community. Here’s all the goodness we’re rolling out in October to give back. 🥳

Open source teams get Tuple for free (forever)

Maintaining open source projects can be an arduous task. Doing it alone makes it even harder.

We want to ease the burden a bit by providing Tuple to any open source team that needs it.

Want to knock out that one lingering issue in your backlog that’s a bit too complex to tackle alone? Are your docs getting a bit stale and in need of a documentation update pairing party? Whatever you’re feeling, we’ve got you covered.

Fill out this form with some basic information about your team and we’ll take care of the rest.

Extended Tuple trials from October 1st - 15th

For the first two weeks of October, we want to help you make your Hacktoberfest contributions by extending our free trial period from 14 to 31 days. This gives you the entire month of October to pair with a fellow dev on your contributions (tip: check out git-pair to share credit).

If you need some help getting started with pair programming, check out our official Pair Programming Guide. It’s full of practical advice that will have you submitting your best work to the open source projects that need it.

Finally, we’d love it if you’d share the open source PRs you made with @tuple! We’re excited to see wait what you can contribute with a pair at your side 💪 .

We want to sponsor your OSS

Tuple wants to join GitHub in helping to make open source more sustainable through their GitHub Sponsorships program.

We’re going to be selecting a few open source contributors each quarter to sponsor, and you could be in the first round!

Fill out this form, follow @tuple, and we’ll announce the sponsors on Twitter by October 7th.

Video sharing enhancements

Sharing your video on Tuple is a great way to enhance communication while pair programming. Tuple supports using the built-in camera, connected webcams, and as of our last release, virtual webcams!

Here are three new ways you can customize and upgrade the video you share:

1. Use your phone for higher quality video

If you’ve got an iPhone or iPad, you can use them as webcams with the help of a super useful app called Camo. (The camera on your iPhone is quite a bit better than most webcams you can buy, so this is a great option.)

Camo lets you adjust the camera lenses, lighting, colors, zoom, crop and focus via your computer. It’s easy to use, and cleverly pushes a lot of the video processing to your phone to reduce load on your computer.

With the recently-added virtual camera support in Tuple, we now support apps like Camo.

To celebrate, Camo is kindly offering a discount on pro licenses. If you want more control of your image, and hi-res video, give it a try.

Using Tuple with Camo

2. Share almost anything as video via virtual camera

Support for virtual cameras opens the doors to all sorts of video sources. You can now use virtual camera generating software to mix in various inputs, including video captured by DSLRs.

For example, in OBS Studio you could choose your DSLR as a video source for a virtual camera, and then select that as the video you share in Tuple.

3. Add a filter with Snap Camera

Ever wanted to pair program with a bearded dragon? You could add Snap Camera as a virtual source in OBS Studio. Go ahead and use your favorite filters to have even more fun while you work together.

Using Tuple with Snap Camera

Virtual camera support brings new ways to use the devices you already have to share video how you want to on Tuple. We hope you enjoy these new options, and have as much fun using them as we have!

2020 Year in Review

First, a brief 2019 recap

We launched Tuple (a remote pair programming app for macOS), in January of 2019.

We began with a closed alpha of ~12 teams, and invited new cohorts from our waiting list every month or so. The app was invite-only until October, when we finally launched a self-serve sign up flow. (If you’re an indie hacker thinking you need to build out a sign up process early on, please consider that we hit five figures of MRR without so much as a pricing page.)

Overall, 2019 was about rapidly shipping basic, tent pole features: webcam support, 3-way calls, dual cursors, a marketing site, and similar.

At the end of the year, the team was still just us three cofounders, and the business felt fairly simple.

And then, 2020

The Ides of March

When we founded Tuple in 2018, it felt clear to us that remote development was on the rise. We figured the market would grow a bit each year, and we could create a nice little business serving this small but growing segment.

By the start of 2020, it looked like we had been correct: we had hundreds of happy, paying customers. If you’d asked me how the business was doing, I would have described our growth in enthusiastic terms.

Then, March happened.

Suddenly, the percentage of developers working remotely rose to “approximately everyone.”

For us, several things blew up at once:

  • Our largest customer told all of its thousands of developers to immediately start working from home, and to use Tuple to support their frequent pairing practice. The intentionally-naive architecture we used to display the list of online users promptly fell over, taking the service offline for nearly everyone.
  • What had been a slow trickle of inbound inquiries for our Enterprise plan turned into a torrent, and I was suddenly staring at an inbox full of leads that wanted to go through a full sales process.
  • Our support volume tripled.

And so, we scrambled:

  • We dropped everything to focus on the scaling issues. We spent a hectic day pushing out hotfixes and constantly refreshing performance graphs and background job queues. If you’d like a full description of that crazy day, check out this podcast episode.
  • We hired a part-time sales person to take that responsibility off my plate (thank goodness).
  • We started devoting a large chunk of time to getting through the support queue each day. Later, we hired part-time help here as well.

Fortunately, by the end of the month, we felt like we had things under control. We’d hired where needed and made our infrastructure more resilient. The business itself was also healthier: in March, we added more revenue than we had in the 14 previous months combined.

It feels weird to acknowledge that we benefited from something so terrible, and I’d trade this reality for one in which Covid never happened in a second. That said, I’m grateful that we were able to provide something that made this year a little easier for folks. We get messages nearly every day saying that pairing over Tuple has made mandatory working from home a bit more tolerable, and that feels great.

New Tuplers

We added three awesome people to our team in 2020:

  • A full-time macOS engineer, Mikey.
  • A part-time sales person, Adam.
  • A part-time support person, Lito.

In addition, we retained security and QA firms to check our work.

Honestly, I had been skeptical about hiring until this year. We’re programmers wielding the awesome power of automation through code! Surely we could get by with just three founders, right?

Sure. Kind of. Except for two things.

First, we were getting close to burn-out. The huge influx of customers in the Spring didn’t just stress our infrastructure and support process, it added mental stress. Tens of thousands of people were suddenly relying on Tuple to get their work done every day. Bugs in production could mean widespread disruption, and we started to feel anxiety around shipping new things. We were also feeling the pain of the tech debt we’d created in 2019, and taking on the necessary refactors was similarly scary.

Second, while I intellectually grasped that adding people to our team would improve our capabilities, I hadn’t truly internalized just how big a difference it could make: Mikey, Adam, and Lito have improved our product and company.

Just one example: they’ve taken difficult work off our plates and done it better than we could. The parts of the sales process that drove me crazy barely bother Adam at all. Mikey dove into our large-scale refactorings and rewrites without fear. And Lito cranks through our sizable support queue while remaining pleasant and helpful.

On top of that, working with these folks has made our day-to-day experience far more fun. Turns out working with great people just makes things straight-up more enjoyable. Consider me a convert to the power of adding awesome people to your team.

Debt payoff

If 2019 was about quickly shipping the first version of lots of features, 2020 was about coming back to battle-harden those things.

In 2020, we shipped near-complete rewrites of huge chunks of the Tuple client:

On top of these large-scale rewrites, we fixed a ridiculous number of bugs and crashes (turns out a real-time streaming application written in C++ that interacts with native OS APIs is, well, a bit tricky). We add more automated tests each month, and each production release is now reviewed by our QA firm before going live.

Fortunately, this hard work has paid off: we’re seeing fewer crashes than ever, and our median call quality rating is a preposterous 5 out of 5.

To be completely honest, I wish we’d shipped more large-scale user-facing features in 2020. But with our cleaner codebase, I think we’re well poised to do a ton of it next year.

A bright future

I think 2021 is going to be a great year for Tuple.

We’ve got a strong team, a clean codebase, and infinite runway. I can’t wait to tackle some of the larger projects we’ve been dreaming about for years now.

Also, I’m having a great time working on this company. My cofounders are amazing, and our team just keeps getting stronger. What’s more, our customers continue to blow me away with how kind and helpful they are. I’m so glad I get to work on something that’s attracted such a great group of users.

I’m excited about the future, and I hope you are too.

Thanks for reading!

Three-player improvements

We’ve taken some big steps toward improving the experience of three-way pairing sessions on Tuple.

Observers can now control the remote machine (and more)!

The increasingly-inaccurately-named observers now have way more abilities:

  • Remote control with keyboard and mouse!
  • Drawing on the screen.
  • Sending text to the host’s clipboard.
  • Triggering the all-important celebration confetti.

There are still a few restrictions that differentiate an observer (the third person to join a call) from a guest (the second person to join), but those are next on the chopping block. Soon enough, we’ll remove those differences entirely, and a call will simply have a host and multiple equally-capable guests.

Dual/multi-cursors work with all pointing devices now

To support our new three-way pairing sessions, we needed a mouse mode that didn’t feel crazy with three participants.

We decided to expand our dual cursor mode into a multi-cursor mode, with one cursor for each participant.

As part of that refactoring, we rewrote the low-level code, fixing some long-standing bugs that prevented some mice/trackpads from using this nifty mode.

Everyone can see who is in a three-way call

All participants can open their Tuple popover and see everyone who’s on the call.

Hosts can kick an observer from the call

Just in case, you know?

Smaller changes:

  • We removed the Highlight click mode. It’s been our least-used mode for a while, and with the new multi-cursor mode, it really felt like it wasn’t pulling its weight. If you really miss the old highlight circles, you can still trigger them by right-clicking when you don’t have cursor control in tag team mode.
  • We eliminated some crashes that could occur when your audio device becomes unavailable.
  • Mouse events are sent to your pair(s) more efficiently.
  • The UI no longer blocks while the webcam starts up.

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.

Demos

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.

Shipping

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!