paint-brush
How to Create Apple Music's Background Blur Effect Using Blurhashby@jana
734 reads
734 reads

How to Create Apple Music's Background Blur Effect Using Blurhash

by JanaAugust 2nd, 2023
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

n this blog post, we'll learn how to create apple music's background blur effect using blurhash.
featured image - How to Create Apple Music's Background Blur Effect Using Blurhash
Jana HackerNoon profile picture


In this blog post, we'll learn how to create Apple Music's background blur effect using Blurhash. I often wonder how Apple adds this blur effect in their app while playing the music. It's super cool if you open the playing music on the Apple Music app. The background blur color changes based on the playing music thumbnail image. This feature intrigued me to find the approach to how to do it. In this blog post, we'll learn how to create this effect.


Getting Started

I assume that everyone knows how to create a Next.js project. If you need help, follow this link to get started. Now we need to install the following packages into our project.


# yarn 
yarn add blurhash react-blurhash sharp

# npm
npm install --save blurhash react-blurhash sharp


Let's start coding. First, we will create an API that takes a URL as a parameter and returns the blurhash string. I will use the Next.js v13 App router to implement this feature. If you are unfamiliar with this App folder approach, use the earlier Page router approach to achieve this behavior.


// app/api/gethash/route.ts

import * as blurhash from 'blurhash'

import { NextResponse } from 'next/server' import sharp from 'sharp';

export async function GET(req: Request) {

    const { searchParams } = new URL(req.url) const url = searchParams.get('url')


if (!url) return NextResponse.json({
    message: 'Required fields is empty'
}, {
    status: 400
})

try {
    const image = await fetch(url);
    const buffer = Buffer.from(await image.arrayBuffer())

    const { data, info } = await sharp(buffer)
        .raw()
        .ensureAlpha()
        .resize(32, 32)
        .toBuffer({
            resolveWithObject: true
        });

    const hash = blurhash.encode(
        new Uint8ClampedArray(data),
        info.width,
        info.height,
        4,
        4
    )

    return NextResponse.json({
        hash
    })
} catch (err) {
    return NextResponse.json({
        error: err
    }, {
        status: 404
    })
}
}


We created a new route handler file inside the app/api/gethash folder. The route handler file name should be route.js|ts. While working with route handler files, you must be very conscious of the page.js file, and the route.js should be at a different level. Check this link for further details.


Let's break the code line by line to understand what is happening.


  • As mentioned above, our route takes the URL as a parameter. We need to get the the URL passed in the query params and check whether the value of the URL is empty or not.


const { searchParams } = new URL(req.url) const url = searchParams.get('url')

if (!url) return NextResponse.json({
    message: 'Required fields is empty'
}, {
    status: 400
})


  • We need to convert our image URL to an array buffer. To achieve this, we need to fetch the image from the source URL and convert it into an array buffer using the method.


const image = await fetch(url); const buffer = Buffer.from(await image.arrayBuffer())


  • Now, pass the buffer into the sharp function. Sharp is an image processing package. We use this package to resize our images to smaller ones because it helps us to generate the hash string faster.


const { data, info } = await sharp(buffer) .raw() .ensureAlpha() .resize(32, 32) .toBuffer({ resolveWithObject: true });


  • Finally, pass the buffer returned from sharp into encode function of the blurhash package. It produces a hash string.


const hash = blurhash.encode( new Uint8ClampedArray(data), info.width, info.height, 4, 4 )


  • Return the hash string from the API. Now we have the API to generate a hash string based on the image URL. Let's start rendering it on the UI. We are going to use the react-hash package. We need to pass the hash string to this package. It then generates the blurred background based on the input URL.


// page.tsx

import Image from 'next/image'
import BlurHash from './components/BlurHash'

async function getBlurHash() {
  const response = await fetch('http://localhost:3000/api/gethash?url=https://plus.unsplash.com/premium_photo-1685077715983-772598c45360?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=200')

  return await response.json()
}

export default async function Home() {
  const { hash } = await getBlurHash()

  return (
    <main className='main'>
      <div className='hash'>
        <BlurHash hash={hash} />
      </div>
      <Image src='https://plus.unsplash.com/premium_photo-1685077715983-772598c45360?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=986&q=80' alt='' width={500} height={500} />
    </main>
  )
}


// components/BlurHash.tsx

'use client';

import React from 'react'
import { Blurhash } from 'react-blurhash';

const BlurHash = ({ hash }: { hash: string }) => {
    return (
        <Blurhash
            hash={hash}
            width={"100%"}
            height={"100%"}
            resolutionX={32}
            resolutionY={32}
            punch={1} 
            />
    )
}

export default BlurHash


Here is the link to my repo. If you have any questions or comments, let me know on Twitter at @jana__sundar or via email at mailtojana23[at]gmail.com. But until next time, happy coding!


Also published here.