SkillAgentSearch skills...

compose-graphics

Advanced Compose visuals - Material 3 Expressive motion physics, AGSL shaders (Android 13+), Canvas/DrawScope generative, graphicsLayer effects.

Install / Use

npx skills add AThevon/genjutsu --skill compose-graphics

Installs into whichever agent you are using.

About this skill
📄

SKILL.md

Installable skill definition

Quality Score

91/100

Category

Design

Supported Platforms

Universal

Our assessment of compose-graphics

compose-graphics scores 91/100 on our quality scale, 81st of 251 Design skills we index (top 33%).

Its SKILL.md is 20 KB long, well organised into 30 sections with 16 code examples: a thorough specification that gives an agent plenty to work with.

It has 370 GitHub stars, a meaningful sign that others use it.

Substance
30/30
Structure
20/20
Description
15/15
Adoption
11/20
Freshness
15/15

Maintenance, license and trust

  • The repository was last updated 26 days ago, so compose-graphics is actively maintained.
  • No license is declared. By default that means all rights are reserved: you can read it, but reusing or redistributing it is not clearly permitted. Ask the author before building on it commercially.
  • Its trust signals score 88/100, with 1 caution from licensing, adoption, age or documentation. These come from repository metadata, not a code audit — read the skill file before letting an agent act on it.

Safety scan

No issues found

Our scan of the whole file found no instruction hijacking, hidden characters, credential access, data exfiltration or destructive commands.

Automated pattern scan on 2026-10-05. It catches known dangerous patterns, not every risk — read a skill before letting an agent act on it.

compose-graphics compared with similar skills

All 4 of these similar skills score higher than compose-graphics; compare them before choosing.

SkillScoreStarsUpdatedFormat
compose-graphics (this skill)by AThevon9137026d agoSKILL.md
Agent-Reachby Panniantong10090.8k19d agoCLAUDE.md
headroomby headroomlabs-ai10074.4ktodayCLAUDE.md
Scraplingby D4Vinci10085.7ktodayMCP Server
crawl4aiby unclecode10084.8k9d agoMCP Server

Frequently asked questions

How do I install compose-graphics?
Run npx skills add AThevon/genjutsu --skill compose-graphics. The install tabs above show the steps for each supported agent.
Which AI agents does compose-graphics work with?
It is written for Universal, as a SKILL.md file. Other agents that read the same format can often use it too.
Is compose-graphics safe to use?
Our scan of the whole file found no instruction hijacking, hidden characters, credential access, data exfiltration or destructive commands. It declares no license and scores 88/100 on trust signals. Skills are instructions an agent will follow, so read the file before installing it and do not approve commands you do not understand.
Is compose-graphics still maintained?
The repository was last updated 26 days ago, so compose-graphics is actively maintained.

name: compose-graphics description: "Advanced Compose visuals - Material 3 Expressive motion physics, AGSL shaders (Android 13+), Canvas/DrawScope generative, graphicsLayer effects." metadata: internal: true

Version-sensitive. Every API name, SDK gate and browser-support claim below was verified on 2026-09-08 against primary sources. What against, and when, is in _jutsu/VERSIONS.md. If that date is old, re-verify before acting on a version number.

Compose Graphics

Advanced Compose visuals: M3 Expressive motion physics, AGSL shaders (Android 13+), Canvas / DrawScope, graphicsLayer effects. Loaded for advanced thesis (shader, expressive, M3 Expressive, AGSL, Canvas, holographic). Foundation: ../compose-motion/SKILL.md covers basics. Concise rules here. Deep-dive in references/.


Decision Tree: Which API for Which Need

| Need | API | |---|---| | Spring physics with bounce / overshoot | MotionScheme.expressive() (M3 Expressive) | | Pixel-level shader | RuntimeShader + Modifier.graphicsLayer { renderEffect = ... } (Android 13+) | | Generative drawing (paths, particles, fractals) | Canvas { drawScope -> ... } | | GPU effects (blur, shadows, color filters) | Modifier.graphicsLayer { renderEffect = ... } or Modifier.blur(...) | | Adaptive system materials (Material You glassmorphism) | Modifier.background(MaterialTheme.colorScheme.surfaceContainerHighest) | | Liquid glass on Android | AGSL shader recipe (no native API like iOS yet) |


Domain 1: Material 3 Expressive

What It Is

The 2025 Material 3 evolution introduces spring-based motion physics replacing fixed-duration tweens. New shape morphing API via androidx.graphics.shapes. New MotionScheme selectable on the theme. Aimed at hero moments, key interactions, brand-defining UI.

MotionScheme

| Scheme | Personality | Use For | |---|---|---| | MotionScheme.standard() | Calmer, less overshoot | Default for chrome, lists, navigation | | MotionScheme.expressive() | More overshoot, longer settle | Hero reveals, FABs, primary CTAs |

Apply on the theme:

MaterialTheme(motionScheme = MotionScheme.expressive()) {
    // children read tokens via MaterialTheme.motionScheme.*
}

Tokens exposed:

| Token | Domain | Spring (damping / stiffness) - standard | ... expressive | |---|---|---|---| | fastSpatialSpec() | Position / size | 0.9 / 1400 | 0.6 / 800 | | defaultSpatialSpec() | Position / size | 0.9 / 700 | 0.8 / 380 | | slowSpatialSpec() | Position / size | 0.9 / 300 | 0.8 / 200 | | fastEffectsSpec() | Opacity / color | 1.0 / 3800 | 1.0 / 3800 | | defaultEffectsSpec() | Opacity / color | 1.0 / 1600 | 1.0 / 1600 | | slowEffectsSpec() | Opacity / color | 1.0 / 800 | 1.0 / 800 |

Spatial vs Effects: spatial = anything physical (height, offset, scale). Effects = visual properties without inertia (alpha, color, elevation). Both families are springs - the effects ones are just critically damped (dampingRatio = 1.0) and much stiffer, so they never overshoot. Only the spatial springs differ between standard() and expressive().

Availability: MotionScheme, MaterialTheme(motionScheme = ...) and MaterialExpressiveTheme are stable in material3 1.4.0 (no opt-in needed). MaterialShapes and the shape-morphing helpers are not - they need 1.5.0-alpha and @OptIn(ExperimentalMaterial3ExpressiveApi::class).

Hero Card Expand (Expressive Springs)

@Composable
fun ExpressiveHero() {
    var expanded by remember { mutableStateOf(false) }
    MaterialTheme(motionScheme = MotionScheme.expressive()) {
        val transition = updateTransition(targetState = expanded, label = "expand")
        val height by transition.animateDp(
            transitionSpec = { MaterialTheme.motionScheme.slowSpatialSpec() },
            label = "height"
        ) { if (it) 400.dp else 100.dp }
        val alpha by transition.animateFloat(
            transitionSpec = { MaterialTheme.motionScheme.defaultEffectsSpec() },
            label = "alpha"
        ) { if (it) 1f else 0f }

        Card(
            modifier = Modifier
                .fillMaxWidth()
                .height(height)
                .clickable { expanded = !expanded }
        ) {
            Box(modifier = Modifier.alpha(alpha)) {
                Text("Detail content", modifier = Modifier.padding(24.dp))
            }
        }
    }
}

Spring Tuning Recipes

| Mood | Spec | |---|---| | Hero reveal | spring(stiffness = Spring.StiffnessLow, dampingRatio = Spring.DampingRatioMediumBouncy) | | Snappy expressive | spring(stiffness = Spring.StiffnessMediumLow, dampingRatio = 0.7f) | | Calm spatial | MaterialTheme.motionScheme.defaultSpatialSpec() (use the token) | | Critical (no overshoot) | spring(stiffness = Spring.StiffnessHigh, dampingRatio = 1f) |

Shape Morphing (MaterialShapes + Morph)

Two different libraries, and the split matters:

  • androidx.graphics.shapes:graphics-shapes (stable 1.1.0) ships RoundedPolygon, CornerRounding and Morph(start, end). Its Morph.toPath(progress) returns an android.graphics.Path.
  • The predefined Material shapes (MaterialShapes.Circle, Pentagon, Cookie4Sided, Sunny, Heart, ...) live in material3, not in graphics-shapes. They are still @ExperimentalMaterial3ExpressiveApi and ship only in androidx.compose.material3:material3:1.5.0-alpha* - they are not in stable material3 1.4.0. material3 also adds Morph.toPath(progress) returning a Compose Path, plus RoundedPolygon.toShape().

Never write .asAndroidPath().asComposePath(): with the graphics-shapes import it does not compile (android.graphics.Path has no asAndroidPath()), and with the material3 import it is a pointless round trip. And because MaterialShapes are normalized to a 0..1 box, the path must be scaled to the draw size - dropping the raw path into GenericShape yields a ~1px shape in the top-left corner.

import androidx.compose.material3.MaterialShapes
import androidx.compose.material3.toPath          // Morph.toPath -> androidx.compose.ui.graphics.Path
import androidx.graphics.shapes.Morph

@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun MorphingBadge(active: Boolean) {
    val morph = remember { Morph(MaterialShapes.Circle, MaterialShapes.Cookie4Sided) }
    val progress by animateFloatAsState(
        targetValue = if (active) 1f else 0f,
        animationSpec = MaterialTheme.motionScheme.slowSpatialSpec(),
        label = "morph",
    )
    Box(
        modifier = Modifier
            .size(96.dp)
            .clip(MorphShape(morph, progress))
            .background(MaterialTheme.colorScheme.primary)
    )
}

/** MaterialShapes are normalized to 0..1: scale the path to the draw size, then centre it. */
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
private class MorphShape(private val morph: Morph, private val progress: Float) : Shape {
    override fun createOutline(
        size: Size,
        layoutDirection: LayoutDirection,
        density: Density,
    ): Outline {
        val path = morph.toPath(progress = progress)
        path.transform(Matrix().apply { scale(x = size.width, y = size.height) })
        path.translate(size.center - path.getBounds().center)
        return Outline.Generic(path)
    }
}

For a static (non-morphing) shape, skip all of this: MaterialShapes.Cookie4Sided.toShape() returns a ready-to-use Shape that already handles the scaling.

When to Use Expressive vs Standard

  • Expressive: hero moments, key interactions, FABs, primary CTAs. 1-3% of UI.
  • Standard: default for the rest of the app. Mixing too much Expressive feels chaotic - every element fighting for attention.

Domain 2: AGSL Shaders (Android 13+)

What It Is

AGSL is Android's shader language. Similar to GLSL with simplifications (half4 instead of vec4, restricted feature set, sandbox-safe). RuntimeShader compiles your AGSL source. Bind to a Compose modifier via Modifier.graphicsLayer { renderEffect = ... }.

Setup

@Composable
fun ShaderEffect(content: @Composable () -> Unit) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
        // Android 12 and below: skip the shader, render content as-is.
        content()
        return
    }
    val shader = remember { RuntimeShader(AGSL_SOURCE) }
    val time by produceState(0f) {
        while (true) {
            withFrameMillis { ms ->
                value = ms / 1000f
            }
        }
    }
    Box(
        modifier = Modifier
            .onSizeChanged {
                shader.setFloatUniform("resolution", it.width.toFloat(), it.height.toFloat())
            }
            .graphicsLayer {
                shader.setFloatUniform("time", time)
                renderEffect = RenderEffect
                    .createRuntimeShaderEffect(shader, "image")
                    .asComposeRenderEffect()
            }
    ) { content() }
}

Always gate on Build.VERSION.SDK_INT >= 33. Pre-Android 13 fallback: render the content unmodified, or use Modifier.blur() / a static gradient overlay.

Recipe: Touch Ripple

AGSL source:

// ripple.agsl
uniform float2 resolution;
uniform float2 origin;
uniform float time;
uniform shader image;

half4 main(float2 fragCoord) {
    float2 toOrigin = fragCoord - origin;
    float dist = length(toOrigin);
    float wave = sin(dist * 0.05 - time * 8.0) * 0.05;
    float falloff = 1.0 / max(dist * 0.01, 1.0);
    float2 dir = toOrigin / max(dist, 0.0001);
    float2 displaced = fragCoord + dir * wave * falloff * 50.0;
    return image.eval(displaced);
}

Compose binding (state-driven time, animated via LaunchedEffect):

@Composable
fun RippleSurface(content: @Composable BoxScope.() -> Unit) {
    if (Build.VERSION.SDK_INT < 33) { Box { content() }; return }
    val shader = remember { RuntimeShader(RIPPLE_AGSL) }
    var origin by remember { mutableStateOf(Offset.Zero) }
    var startMs by remember { mutableStateOf<Long?>(null) }
    val time by produceState(0f, startMs) {
        if (startMs == null) { value = 0f; return@produceState }
        val begin = startMs!!
        while (true) {
            withFrameMillis { now ->
                value = (now - begin) / 1000f
            }
        }
    }
    Box(
        modifier = Modifier
            .onSizeChanged {
                shader.setFloatUniform("resolution", it.width.toFloat(), it.height.toFloat())
            }
            .pointerInput(Unit) {
                detectTapGestures { tap ->
                    origin = tap
                    startMs = System.currentTimeMillis()
                }
            }
            .graphicsLayer {
                shader.setFloatUniform("origin", origin.x, origin.y)
                shader.setFloatUniform("time", time)
                renderEffect = RenderEffect
                    .createRuntimeShaderEffect(shader, "image")
                    .asComposeRenderEffect()
            },
        content = content
    )
}

Recipe: Holographic Gradient

uniform float time;
uniform shader image;

half4 main(float2 fragCoord) {
    half4 color = image.eval(fragCoord);
    float n = fragCoord.x * 0.01 + fragCoord.y * 0.005 + time * 0.3;
    half3 rainbow = half3(
        sin(n * 2.0) * 0.5 + 0.5,
        sin(n * 2.0 + 2.094) * 0.5 + 0.5,
        sin(n * 2.0 + 4.188) * 0.5 + 0.5
    );
    half luma = dot(color.rgb, half3(0.299, 0.587, 0.114));
    return half4(mix(color.rgb, rainbow * luma * 2.0, 0.5), color.a);
}

Multiplies the underlying view's luminance by a phase-shifted RGB sine wave - shimmer over text or icons. time animates the wave drift.

Recipe: Liquid Glass / Glassmorphism

Android lacks a native iOS-26-style glassEffect. Compose this with blur + chromatic aberration + soft edge tint:

uniform float2 resolution

Truncated for display — read the full file on GitHub.

Related Skills

View on GitHub
GitHub Stars370
CategoryDesign
Updated26d ago
Forks27

Languages

Python

Trust signals

88/100

From repository metadata: license, adoption, age and documentation. Not a code audit — see the Safety scan above for what the skill file itself contains.

1 medium