Deep Breathing & Mindfulness on iOS and Android

"A language-independent meditation app to help ease the world's anxiety"

Live | GitHub


My Role:

Lead Developer / Cofounder

Team Size: 2

Myself + 1 Product Designer

Time Frame:

November 2020 - March 2021:
> 200 Hours

User Testing:

5.9 Million views on Tiktok

Technologies Used:

  • React Native
  • Expo
  • JavaScript
  • CSS
  • Figma
  • Google Firebase
  • Apple Developer Program
  • Sign In With Facebook


This mobile app was developed to help ease the world's stress and anxiety.

  • Memoir promotes mindfulness and serenity through deep breathing and meditation exercises. Users can sign in with their Facebook, Apple ID, or email to save their favorite exercises and track their personal exercise progress.
  • Users have a clean and simple-to-use UI to play and pause exercise videos, set timers, and save favorites.
  • Users have access to a variety of breathing exercises and daily recommendations.
  • Currently in beta testing on TestFlight for iOS, and works on Android via Expo.

Project Summary:

  • Memoir breathing exercises use visual cues to be followed by anyone in the world, regardless of language.
  • Beautiful designs and gestures enhance the overall customer experience.
  • Personalized user progress tracking add to the user experience and help to build better habits.
realyzer rental property calculator
Variety of Breathing Exercises Content
Home Screen
Breathing Exercise Player
Setting a Timer
Favorites Library
Delayed Signup Prompts
Animated User Stats

Challenges Faced:

Caching the Downloaded Image and Video Assets from Firebase Storage using Expo FileSystem API

The following code gist was shrunken from the original code for simplification. The original uses a regular expression checker to categorize assets as "images" or "videos".

  • Firstly, the specifically requested image and videoFile assets are passed down as props to the individual Exercise components.
  • useState() hooks are then initialized to set the actual image and videos to be rendered for later on. The useEffect() hook will run the initial caching function on mount.
  • cacheAsset is an async function, that first checks if the file already exists within the cache to prevent unnecessary download requests, saving bandwidth.
  • If it wasn’t cached, the download URL of the file is retrieved from Firebase Storage. The URL is used to download the file to the device’s cache using the Expo FileSystem API. The useState() states are then stored using the newly downloaded assets.
  • Finally, with the stored states, the cachedImage is used to render the exercise image. When you press on that image, it navigates to the ExerciseVideo screen along with the cachedVideo passed down as a route.param.

UI: User Stats Scroll-Up Numbers Animation

How I implemented the custom animation for the numbers scrolling up:

  • I first made a function to render a short vertical bar of numbers by dividing each specific stat number by 5.
  • Then, I used a MaskedView component from Expo to create a mask for the bar to hide behind.
  • Whenever this specific screen was focused by the user, a setTimeout triggers the transform animation using Animatable.
  • With wild cases such as integers ranging from 0 to long-digit integers like 1,000,000 or beyond, I implemented a custom width solution using conditionals for responsive widths.
Animated User Stats

Dynamically Programmed “Moving Target” Goals & Congratulation Alerts

An alert dialog pops up whenever the user hits their "moving target" goals, which would always increase as the current benchmarks were met.

  • The practice time goals increase from a 30 minute goal, to a 2 hour goal, to a 5 hour goal, and then increments by 5 hours indefinitely.
  • Those conditional rules were stored in a variable and used to trigger the alerts whenever the goals were met and the user visited the stats page.
  • The "dismissed" state of the alert begins as 'false' and is then set to 'true' when the user dismisses it.
Congratulations Alert!

Used Google Firebase’s Realtime Database to Store Personalized Favorites and Progress Objects

Users can keep track of their exercise goals and progress such as hours completed, sessions completed, and best streaks achieved.

  • The number of seconds a user spends on any timed exercises are incremented, which are then converted to minutes and hours as the user progresses.
  • The user's "Sessions Completed" tracker increments whenever a user finishes a timed exercise.
  • The user's "Current Streak" and "Best Streak" are incremented whenever they start any exercise on the next calendar day. This is determined by comparing the last tracked date with a current new Date() Object.
rental property calculator mapbox geocoding address to coordinates featurerental property calculator mapbox geocoding address to coordinates feature
Favorites and progress correspond to each user's unique user ID

UI/UX Solution: Horizontal ScrollViews used for Custom Option Selectors

In order to set timers for breathing exercises, I implemented a horizontal options selector to choose timed intervals. My function renders an array of TouchableOpacity's onto the screen and is hidden/revealed when the user presses the timer icon. Selecting a time option starts the timer and hides the options automatically.

Another horizontal ScrollView was used to set the timer for the Meditation Timer Screen.

I created a custom "scroll wheel" by gathering the measurements for the contentOffset.x, the contentSize.width, and the layoutMeasurement.width and calculating the percentage of space used within the entire ScrollView.

This homemade "scroll wheel" includes haptic feedback to ensure the user is interacting with the adjuster.

Horizontal 'Wheel' to Set Timer
Memoir Splash intro screen
Thanks for visiting!