↩ Experiments

Dynamic Island

November 2023

music album art

Discover

Elior

0:00
0:00

Springiness

I think that the elasticity in Apple's Dynamic Island feels so good because it mimics natural movement principles. The slight spring effect and the end of each transition makes the island feel like a real object being compressed and expanded.

Initially, I tried to use a keyframe animation for the island by overextending the scale of the island and then scaling it back to the original size near the end.

@keyframes spring {
    0% {
        transform: scale(1);
    }
    80% {
        transform: scale(1.2);
    }
    100% {
        transform: scale(1);
    }
}

This led to a lot of tinkering with the timing function of the animation and the scale values to get the right feel. I eventually scrapped this idea.

I started messing with cubic-bezier timing functions and found that you can actually create an 'inverted' curve to simulate a spring effect:

spring graph

Overshooting the scale of the island and then scaling it back to the original size with this timing function felt much more natural than the keyframe animation. The rest of the animations for the island are a combination of filter: blur, opacity, and scale.

Soundwaves

The animation shown when music is actively playing is created using a flexbox container filled with rectangles that each animate to a random height every 300ms. I also capped the maximum height lower for bars further from the center to make sure that large waves weren't appearing on the edge of the container.

const intervalIds = barHeights.map((height, index) => {
    return setInterval(() => {
        const newHeights = [...barHeights];
        if(index < 2 || index > 6) {
            newHeights[index] = Math.floor(Math.random() * 10) + 1;
        } else if(index < 3 || index > 5) {
            newHeights[index] = Math.floor(Math.random() * 15) + 1;
        } else {
            newHeights[index] = Math.floor(Math.random() * 20) + 1;
        }
        setBarHeights(newHeights);
    }, Math.floor(Math.random() * 300));
});

return () => {
    intervalIds.forEach((intervalId) => clearInterval(intervalId));
}; 

Acknowledgements

Obviously, credit to Apple's mobile implentation of the dynamic island for the inspiration. One of the best software feature-adds from Apple for the last few years in my opinion.