DEV Community

Cover image for focused breathing - A CSS animation to help with meditation and focused breathing exercises
Shannon Crabill
Shannon Crabill

Posted on • Originally published at

focused breathing - A CSS animation to help with meditation and focused breathing exercises

What I built

My Digital Ocean / DEV hackathon submission is focused breathing a CSS animation to help with meditation and focused breathing exercises.

Category Submission

Program for the People

App Link or which redirects to the app hosted on Digital Ocean.


Screenshot of focused breathing in its default state.

The timing of the animation (expanding, holding, and contracting) is set to 8 seconds by default. The timing can be changed using the input field.

Here's a gif of the app in action.


focused breathing includes a circle div that expands for 2 seconds, holds it's shape for 2 seconds, then contracts to its original size for 4 seconds. The breathing exercise involves inhaling through the nose as the circle expands. Holding the breath. Then exhaling through the mouth as the circle contracts.

Link to Source Code

GitHub logo scrabill / focused-breathing

A CSS Animation to help with focused breathing exercises

Permissive License



I recently learned about focused breathing exercises as part of a meditation or calming routine. I kept forgetting the timing of how long to inhale, hold long do I hold, etc. So, I decided to make a little app to help me with the pacing.

I've been having fun experimenting with CSS animations and thought this would be an excellent opportunity to learn about the animation property, transitions, and @keyframes.

How I built it

I built *focused breathing* in two parts. The first version was an experiment with CSS animations and @keyframes. I wanted to see if I could achieve the transitions and cadence I wanted with only HTML and CSS. It was possible! No Javascript was needed for the original version, which can be found on CodePen.

For the second part, I wanted to see if I change the duration of the animation (which is in the CSS file) based on input from the user. I know I could get input values from the HTML file with Javascript, but could I pass those updated variables back to the CSS and render it to the page. It turns out this was possible too!

Here's how I approached building parts one and two and what I learned along the way.

Part One - HTML & CSS

Before I started coding, I was helpful for me to write down high-level steps of the focused breathing technique I had been taught.

It goes like this.

  • Inhale through the nose
  • Hold the breath
  • Exhale through the mouth for 4 seconds

The amount of time to inhale of hold the breath may not matter, but to make it easier from a coding perspective, 2 seconds to inhale and 2 seconds to hold a breath seemed reasonable.

Visually, there would be a small to medium-sized circle that would expand, cueing you inhale and contract when it was time to exhale.

Visualizing the Animation

Using @keyframes was the best want to handle the expanding and contracting of the circle so that I control the timing and pacing of each step. With @keyframes the stops or offsets range between 0% and 100%. The beginning or start of the animation would be 0% stop and the end would be 100%. Or, any number in between.

I've worked with @keyframes on other projects and had a difficult time visualizing what code I needed to write to achieve what I visualized in my mind.

It makes sense to map out what I wanted to happen like a timeline. A line segment with two endpoints could represent the timeline of the animation. The left endpoint would be the beginning of the animation cycle and the right, the end.

Some quick labels, notes and visuals and I have a timeline that looks like this.

Translating the Timeline to @keyframes

Looking at the timeline above, it may look like we need 8 or 4 steps in the animation, but this is not the case. Each offset point in a @keyframe animation is a point where properties can be changed from their original values.

The circle starts small, then it’s changed to be larger than it was originally, then it holds that size, then it shrinks down to the size it was originally and the animation starts over. The @keyframe only needs 2 offset points (at the 25% and 50% marks) and the original styling of the circle handles the starting (and ending) visuals.

Similar to grouping other CSS attributes, multiple properties and offsets can be set at one time within the @keyframe declaration.

@keyframes breath {
 25%, 50% {
    background-color: lightpink;
    width: 200px;
    height: 200px;
    border-radius: 100px;
Enter fullscreen mode Exit fullscreen mode

And, to make it a bit easier on ourselves, let’s divide the line into 8 even parts (1 part for each second of the animation).

The timing of the changes and width and height of the circle meant that I couldn't

The expanding and contracting of the circle can be handled with @keyframes.

Part 2 - Javascript

For the second part of this project, I wanted to add some customization. To start, I wanted to see if I could change the duration of the animation—which was 8s to start—to another value. Building an input field was straight forward, but how could that value get updated in the animation property?

In doing some Googling, I was reminded that CSS variables could be accessed and updated with Javascript using getComputedStyle and getPropertyValue. I was already using CSS variables for colors and sizes, so created a new one for timing.

:root {
    --timing: 8s;
Enter fullscreen mode Exit fullscreen mode

And updated my animation property to include that variable (var(--timing)) instead of the hardcoded value (8s)

div {
   animation: breath var(--timing) ease infinite none running
Enter fullscreen mode Exit fullscreen mode

Visually, nothing changed, which meant it worked! I could double-check the value of --timing by running the following in the Console.

getComputedStyle(document.documentElement).getPropertyValue('--timing') // 8s
Enter fullscreen mode Exit fullscreen mode

And I could change it with the following and see the animation speed up dramatically.'--timing', '1s');
Enter fullscreen mode Exit fullscreen mode

Then, by adding an input field onto the page, I could grab the value of that input, pass it into .setProperty and update the CSS.

Wrap Up

Overall, I learned a lot about @keyframes with this project! Drawing out what I had in mind made coding go smoother with less trial and error.

Looking back at this project, I tried for the first time, or became more comfortable with:

  • CSS Grid (centering things, amiright?)
  • CSS Animations (the animation and @keyframes property)
  • Manipulating CSS variables with Javascript ( getComputedStyle and getPropertyValue)
  • Continuous deployment (yikes to manually copy and pasting files like I usually do)

For future enhancements, some thoughts are:

  • The ability to change other variables (hold time is longer, shorter, etc)
  • The ability to start and stop (or, incorporate a timer for 5 mins of focused breathing, etc)
  • Sounds or music so accompany to indicate when you breathe in, breath out, etc.
  • A detailed tutorial on how to build your own focused breathing animation/app from scratch

Additional Resources/Info

Top comments (10)

crimsonmed profile image
Médéric Burlet

Water Breathing, Eleventh Form: Dead Calm.....
No on a serious note it's a very clean animation I think you could add a small setting to let people define colors or to have random sets of colors for a little flare

scrabill profile image
Shannon Crabill

Thank you! Random colors would be a nice add. I did that for another project so it would be easy to implement here.

That and a dark mode variation.

georgedoescode profile image
George Francis

I love this! Really satisfying animation :)

dmahely profile image
Doaa Mahely

Great job!

bobbyiliev profile image
Bobby Iliev

That is pretty cool! Great job!

I just did a quick mindful breathing session!

oliverleitner profile image
Oliver Leitner

and i always thought you cannot make a useful css demo. you proofed me wrong, thanks=)

scrabill profile image
Shannon Crabill

It means a lot to hear you say that. Thank you.

willholmes profile image
Will Holmes

Haha love this!

sandraahlgrimm profile image
Sandra Ahlgrimm

Thank you. That is very cool!

raihaniiuc profile image

This is superb.