Doing the Impossible — Building a Persistent Audio Player in Ruby on Rails

Today we’re going to learn how to build a Ruby on Rails app that accomplishes what some folks think is impossible in a multi-page application — persisting an audio player while navigating around an application.

We’ll use Ruby on Rails and Turbo to accomplish this, but we could use Turbo’s predecessor, Turbolinks to achieve the same result, and Rails is only incidental to the finished project. We could just as easily use Turbo with any other “multi-page” framework and deliver the same experience.

When we’re finished, our application will allow users to create and manage Ideas along with a persistent audio player tuned to a white noise internet radio station, to help them focus while they generate ideas.

Users will be able to start and stop the audio using a standard <audio> input. When audio is playing, it will continue playing as the user navigates around the application.

The application will look like this:

A screen recording of a web page. On the web page is an audio player and interface to create and view Ideas. The user clicks play on the audio player and then navigates through pages of the idea management interface. While they navigate, the audio player continues playing.

After we’ve built our application, we’ll spend a time talking about myths in web development, and how to avoid falling into the expert-led myth-trap.

This article assumes basic familiarity with Ruby on Rails, but no prior experience with Turbo is required, and you should be able to follow along even if you’ve never seen Rails code before.

Let’s get started.

Application Setup

To begin, we’ll create a standard Ruby on Rails app, install Turbo, and scaffold an Ideas resource that we’ll use to demonstrate the “multi-page” part of the application.

rails new rails_focused_ideas
cd rails_focused_ideas
bundle add turbo-rails
rails turbo:install
rails g scaffold Idea title:string description:text
rails db:migrate

To finish installing Turbo, open app/packs/application.js and update it like this:

import Rails from "@rails/ujs"
import "@hotwired/turbo-rails"
import * as ActiveStorage from "@rails/activestorage"
import "channels"

Rails.start()
ActiveStorage.start()

Here we’ve removed any references to turbolinks and added the import "@hotwired/turbo-rails" line to use Turbo throughout our application.

When you’ve finished setting up the application, start your rails server (rails s from your terminal) and navigate to http://localhost:3000/ideas.

Add audio player to layout

Next up, we’ll add an audio element to our application layout (app/views/layouts/application.html.erb), just after the opening <body> tag:

<audio 
  controls
  src="http://uk1.internet-radio.com:8267/listen.pls&t=.m3u">
      Your browser does not support the
      <code>audio</code> element.
</audio>

The src attribute in this code is a random white noise internet station and it could go down tomorrow or ten years from now. You’re free to use whatever you like for the source, including a local file accessible to your Rails server or any other internet radio station you like.

Now if we refresh the page, we’ll see our audio element is there and we can start and stop it, but every time we navigate to a new page, the audio stops playing.

How do we make it persist?

Persisting the audio player across page turns

To make the audio persist across page turns, update the <audio> element like this:

<audio 
  data-turbo-permanent
  id="white-noise-player"
  controls
  src="http://uk1.internet-radio.com:8267/listen.pls&t=.m3u">
    Your browser does not support the
    <code>audio</code> element.
</audio>

We’ve done the impossible and made our audio element persistent across page turns by making two changes.

First, we added data-turbo-permanent to the element to tell Turbo that this element should persisted across page loads. Next, we added an id to the audio element, which Turbo uses to match the element when it renders pages.

With these small changes in place, we can refresh the page, start up the audio player, and then navigate around our application, clicking links and navigating through the browser history to our hearts content without ever stopping the audio player.

A screen recording of a web page. On the web page is an audio player and interface to create and view Ideas. The user clicks play on the audio player and then navigates through pages of the idea management interface. While they navigate, the audio player continues playing.

Magical.

Why write this article?

The problem we solved today — building a persistent audio player with Turbo(links) — has been solved for a long time.

Why take the time to write up something that’s so simple?

Because, as we saw in the tweet linked in the introduction, dangerous misconceptions exist in web development. These misconceptions are often spread by folks who want you to believe that their way is the One True Path.

Downstream of this bad information are developers researching problems who absorb the bad information and trust it because it comes from someone who has such a big platform that they must know what they’re talking about.

Some of those developers will then make decisions based on the bad information and justify their decisions in conversations with other developers by repeating the bad information until it is eventually picked up and presented as truth by a new flock of experts.

The bad information continues flowing, poisoning the conversation for years to come, turning into myths based on half-truths, misunderstandings, and overconfidence.

An image of two circles, connected by arrows. The first circle reads 'Experts share bad information' and the second circle reads 'Bad info spreads through the community'

This is how myths like “Rails doesn’t scale” propagate and persist, when even a cursory amount of research disproves the idea.

Defending against myths

You can avoid falling for these myths by remembering that everyone writing about web development on the internet only knows a tiny fraction of all that there is to know about web development.

No one is an expert on every language and every method for building web applications. Instead, all of us, at best, know a lot about a few things, and a little about a lot more things.

We make decisions and recommendations based on that very limited knowledge, which means we can never confidently speak in absolutes. We can only share what we think is best, based on our own knowledge, for better or worse.

There is no One True Path in web development. Many different valid solutions exist, each with their own set of benefits and tradeoffs.

Fortunately, we don’t need One True Path. Many paths can lead to the same destination.

A venn diagram with two sides that don't overlap. One side reads 'The framework you choose' and the other reads 'Things that make your product successful'

With this in mind, try to follow these rules to be a good consumer when you’re exposed to web development content:

  • Discard any absolute opinion about one technology or framework being “the best” way to solve any particular problem
  • Don’t listen to people who frequently speak in absolutes about languages or frameworks without contextualizing their opinion
  • When comparing languages or frameworks, anyone who says “[x] can’t be used for [y] you have to use [z] instead” is probably just trying to sell you on [z]
  • If someone refuses to acknowledge the weaknesses of their chosen approach, assume they’re not being honest
  • When someone is an expert on a tool, you can probably trust what they’re saying about that specific tool
  • If you don’t like a particular tool or framework there are probably a dozen other tools to solve the same problem that you can use instead

Wait… aren’t you presenting yourself as an expert? Why should I listen to you?

Fair. Since I am 1) on the internet and 2) talking about web development, I should share what my particular motivations and interests are so you can have that information as you consider this article.

I don’t have a big platform (~150 followers on dev.to and the same on Twitter, and ~2,000 people per month reading my blog). I write weekly about Ruby on Rails and the broader Rails ecosystem to a very small audience.

My writing is mostly aimed at helping newer Rails developers learn how to solve common UX problems that they’ll encounter as they work, and to share some of the modern tools available in the broader Rails ecosystem that enable performant, modern user experiences without needing to turn to a SPA.

My opinions on Rails are built from ~a decade of working on large production Rails applications and I consider myself to be Pretty Good at building Rails-powered web applications.

I encourage folks to consider Rails as an alternative to SPAs and JavaScript-everything not because I think JavaScript frameworks are bad, but because I think that Ruby and Rails are wonderful tools, and I’d like more developers to experience them.

That’s me — now you can decide whether to throw my thoughts on everything in the trash or not.

Wrapping up

Today we built a persistent audio player inside of a simple, multi-page application — from one perspective, an impossible task, from another, very simple.

We used this impossible/simple problem to talk a little about the myths of web development, and how experts with large platforms can spread those myths (often unintentionally!) to their audience. These myths then propagate through the community at large, and cause unnecessary, impossible to resolve arguments for years to come.

While we can’t stop experts with large platforms from sharing bad information, as developers, we can be informed about how we consume content. We can be more thoughtful about how we approach learning and growing when we know that absolutes rarely exist and objectively correct answers about which language or framework to use are few and far between in the world of web development.

As always, thanks for reading!

Better people, better products newsletter.

Enter your email to sign up for a once-monthly newsletter from me with my latest writing, other pieces I find interesting, and special bonus content.

Powered by Buttondown.