
m3-loading-indicator is a canvas-based loading animation library that creates the Material Design 3 expressive loading indicator in your React, Svelte, Vue 3, or vanilla JavaScript project.
It ports the full animation system from Android’s material-components-android library, including exact spring force parameters, SVG path geometry from official Material Design asset files, and the dual rotation math from LoadingIndicatorAnimatorDelegate.java.
Features:
- Renders seven morphing shapes in sequence with spring-driven transitions.
- Matches the Android SpringForce stiffness and damping ratio parameters exactly.
- Uses pixel-accurate SVG path geometry from official Material Design asset files.
- Draws on an HTML canvas with automatic HiDPI scaling.
- Supports a contained variant with a circular background behind the indicator.
Vanilla JavaScript Usage:
This example creates a 56px canvas indicator and keeps animation work inside requestAnimationFrame.
<p><canvas id="billing-loader"></canvas></p>
<script type="module">
import {
M3Animator,
getMorphedShape,
drawIndicator,
setupCanvas,
} from "https://cdn.jsdelivr.net/npm/@alerix/m3-loading-indicator/dist/core/index.js";
const canvas = document.querySelector("#billing-loader");
const size = 56;
// Set up a HiDPI-aware 2D canvas context.
const ctx = setupCanvas(canvas, size);
// Create the animation controller.
const animator = new M3Animator();
function renderBillingLoader(timestamp) {
animator.update(timestamp);
// Convert the current morph value into a drawable shape.
const shape = getMorphedShape(animator.morph);
drawIndicator(ctx, size, shape, animator.rotation, {
color: "#6750A4",
sizeRatio: 0.8,
contained: true,
containerColor: "#F3E8FF",
});
requestAnimationFrame(renderBillingLoader);
}
requestAnimationFrame(renderBillingLoader);
</script>React Usage:
React projects can render the default loading indicator with one component import. The default output uses a 48px canvas and inherits the current text color from its parent.
import { M3LoadingIndicator } from "@alerix/m3-loading-indicator/react";
export default function CheckoutStatus() {
return (
<div className="checkout-status">
{/* 48px indicator that uses currentColor */}
<M3LoadingIndicator />
</div>
);
}Vue Usage:
<script setup>
import { M3LoadingIndicator } from "@alerix/m3-loading-indicator/vue";
</script>
<template>
<section class="sales-card">
<header class="sales-card__header">
<span>Revenue snapshot</span>
<!-- Bind numeric props with :prop syntax -->
<M3LoadingIndicator
:size="32"
color="#2563EB"
:speed="1.2"
/>
</header>
</section>
</template>Svelte Usage:
Svelte 5 route transitions can show a loader while a page-level request resolves. The snippet imports the Svelte component and keeps the indicator markup close to the loading message.
<script lang="ts">
import M3LoadingIndicator from "@alerix/m3-loading-indicator/svelte";
</script>
<div class="route-loading" aria-live="polite">
<!-- The loader inherits layout from the surrounding UI -->
<M3LoadingIndicator size={40} color="#9333EA" />
<span>Loading account details</span>
</div>Configuration Options (Props):
size(number): Sets the CSS pixel size of the indicator. The default value is48.color(string): Sets the fill color of the morphing shape. The default value is"currentColor".sizeRatio(number): Sets the shape-to-container ratio. The default value is0.79, which maps the 38dp shape into a 48dp container.speed(number): Sets the animation speed multiplier. The default value is1.paused(boolean): Stops the animation while the component remains visible. The default value isfalse.contained(boolean): Draws a circular background behind the indicator. The default value isfalse.containerColor(string): Sets the circular container background color. The default value is"rgba(0,0,0,0.08)".
API methods:
import {
M3Animator,
Spring,
getMorphedShape,
drawIndicator,
setupCanvas,
getShapes,
lerpShapes,
SHAPE_COUNT,
DURATION_PER_SHAPE_MS,
CONSTANT_ROTATION_DEG,
EXTRA_ROTATION_DEG,
} from "@alerix/m3-loading-indicator";
// Create a core animation controller for custom canvas renderers.
const checkoutAnimator = new M3Animator();
// Advance the morph and rotation state from a requestAnimationFrame timestamp.
checkoutAnimator.update(performance.now());
// Read the current rotation and morph values.
const checkoutState = checkoutAnimator.getState();
// Reset the animation timeline and spring state.
checkoutAnimator.reset();
// Create a HiDPI-aware 2D canvas context.
const canvas = document.querySelector("#checkout-loader");
const ctx = setupCanvas(canvas, 56);
// Convert the current morph number into a drawable point array.
const currentShape = getMorphedShape(checkoutState.morph);
// Draw the indicator on a canvas context.
drawIndicator(ctx, 56, currentShape, checkoutState.rotation, {
color: "#6750A4",
sizeRatio: 0.79,
contained: true,
containerColor: "#E8DEF8",
});
// Read all normalized Material shape point arrays.
const materialShapes = getShapes();
// Interpolate between two shape point arrays.
const halfwayShape = lerpShapes(materialShapes[0], materialShapes[1], 0.5);
// Create the lower-level spring primitive for custom motion experiments.
const spring = new Spring(200, 0.6);
// Set the spring target value.
spring.target = 1;
// Advance the spring by one 60fps frame.
spring.step(1 / 60);
// Reset the spring position, velocity, and target.
spring.reset();
// Read exported animation constants for custom renderers.
console.log(SHAPE_COUNT);
console.log(DURATION_PER_SHAPE_MS);
console.log(CONSTANT_ROTATION_DEG);
console.log(EXTRA_ROTATION_DEG);Alternatives:
- 30+ Loading Spinner Libraries In Pure JavaScript And CSS
- 10 Best Loading Spinner/Indicator Libraries for Pure CSS & JavaScript
- Flexible Customizable Loading Spinners for Web Apps
- Google-Style 4-Dot Bounce Loading Indicator with Pure CSS/CSS3
- Basic Loading Screen For Material Design Lite
- Minimal Text-based Loading Indicator With JavaScript
- Free JavaScript & CSS Libraries For Google Material Design






