Mesh

A desktop editor for creating mesh gradients in Compose, built with Compose Multiplatform and the Jewel UI standalone toolkit. Gradients are rendered using Compose's official MeshGradientPainter, with bicubic color interpolation and automatically inferred Bezier control points.
Features and controls
- Grid: Adjust between 2–64 rows and columns, or distribute points evenly.
- Control points: Drag points on the canvas or edit
X/Yoffsets in the side panel. Double-click the canvas to hide or show points, and toggle edge constraints to lock perimeter points to the canvas bounds. - Palette: Add hex colors to the saved palette, remove unused ones, and assign colors per point.
- Canvas: Configure canvas dimensions (fixed size or fill) and background color.
- Undo/redo: Undo with ⌘Z (Ctrl+Z) and redo with ⇧⌘Z (Ctrl+Shift+Z or Ctrl+Y). Covers point, grid, blur, and background edits, including ones an agent makes.
- Export: Copy Kotlin code for an official
MeshGradientPainterand ameshGradientModifierthat includes the current blur and required imports. Apply it to a sized Composable withModifier.then(meshGradientModifier), or export a PNG at 1x, 2x, or 3x scale. Both live in the export control at the end of the title bar, which names the pixel size it will produce. - Persistence: Grid state and saved colors autosave locally to a Room/SQLite database. Documents save themselves to their
.meshfile a second after each edit; the title bar says nothing while saves succeed and shows a Couldn't save retry when one fails. - Agent access (MCP): Coding agents can read and edit the open mesh through an MCP server. See below.
Roadmap
- Save and load configurations as documents
- Grain overlay
- Presets
- Gradient animation
- Undo/redo
Agent access (MCP)
Mesh can serve an MCP server over Streamable HTTP so an agent can edit the gradient you are looking at. It is off by default: the agent chip at the end of the title bar says Agents off until you open it and turn on Let agents edit this mesh, where you can also change the port (default 7777) and copy the URL. The setting is remembered across launches. Once an agent connects, the chip names it, and turns red if the port is already taken. The server listens only on localhost and rejects requests from other origins.
| Tool | What it does |
|---|---|
get_mesh |
Returns the grid, canvas settings, saved colors with their IDs, and each point's position and color; at most 256 points per call, so a bigger grid needs a rowStart/rowEnd/colStart/colEnd region holding at most 256 points, and a larger region is rejected |
update_point |
Moves and/or recolors the point at a row and column |
add_color |
Adds a hex color to the custom palette and returns its ID |
undo / redo |
Undoes or redoes the last edit, whether you or the agent made it |
render_png |
Renders the mesh to a PNG so the agent can see the result |
Connect from Claude Code:
claude mcp add --transport http mesh http://127.0.0.1:7777/mcp
Other clients take the same URL as a remote (HTTP) MCP server.
Running and packaging
Run from the command line (or use the .run/desktopApp.run.xml run configuration in IntelliJ IDEA):
./gradlew :composeApp:run
Build an unsigned macOS DMG for local testing:
./gradlew :composeApp:packageDmg
Build a signed and notarized release DMG (see docs/RELEASING.md for signing setup):
./gradlew :composeApp:notarizeDmg
