Mermaid handDrawn Look Option: How to Enable Sketch-Style Diagrams with Deterministic Rendering
The handDrawn look option in Mermaid enables sketch-style diagram rendering via Rough.js, while handDrawnSeed supplies a fixed random seed to Rough.js to ensure identical jitter patterns across renders.
The mermaid-js/mermaid repository supports two visual rendering modes for diagrams: the default crisp vector style and an organic, hand-sketched aesthetic. When you enable the handDrawn look option, Mermaid utilizes the Rough.js library to perturb lines and fills with randomized jitter, creating an informal, pencil-drawn appearance. To prevent this randomization from causing visual diffs between renders or test runs, the handDrawnSeed configuration parameter locks the pseudo-random generator to a specific starting value.
What Is the Mermaid handDrawn Look Option?
Mermaid diagrams render in one of two distinct visual styles controlled by the look configuration property defined in packages/mermaid/src/schemas/config.schema.yaml (lines 79-86).
- classic: Default SVG rendering with precise geometry and exact paths.
- handDrawn: Rough.js-generated shapes featuring randomized line perturbations, sketchy outlines, and hachure fills.
When look: 'handDrawn' is active, the rendering pipeline checks node.look !== 'handDrawn' within individual shape files (such as rectWithTitle.ts and waveRectangle.ts) to determine whether to apply Rough.js styling. This ensures that only diagrams explicitly requesting the sketch style receive the randomized treatment, leaving classic diagrams unaffected.
How handDrawnSeed Ensures Consistent Randomized Rendering
By default, Rough.js initializes with a random seed of 0, which causes it to select a new random value on every render. This results in diagrams that look slightly different each time—problematic for visual regression testing, automated documentation generation, or any workflow requiring deterministic output.
The handDrawnSeed configuration property (numeric, default 0) solves this by injecting a fixed seed value into all Rough.js calls throughout the rendering pipeline:
- In
packages/mermaid/src/rendering-util/rendering-elements/shapes/handDrawnShapeStyles.ts, thesolidStateFillfunction (lines 6-15) returns fill styles containingseed: handDrawnSeed. - The
userNodeOverrideshelper (lines 95-108) merges the seed into node style maps. - Edge rendering modules, such as
packages/mermaid/src/rendering-util/rendering-elements/edges.js(line 517), retrieve the seed viagetConfig()and pass it to Rough.js options.
When you provide a specific non-zero integer (e.g., handDrawnSeed: 12345), Rough.js receives identical seed values for every element, reproducing the exact same random perturbations on every render while preserving the sketchy visual style.
Configuration and Usage Examples
Enabling handDrawn in HTML/JS
Initialize Mermaid with the look and handDrawnSeed options to enable deterministic sketch-style rendering in browser environments:
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
<script>
mermaid.initialize({
startOnLoad: true,
theme: 'default',
look: 'handDrawn',
handDrawnSeed: 12345
});
</script>
<div class="mermaid">
graph TD
A[Start] --> B{Decision}
B -->|Yes| C[Result]
B -->|No| D[Alternative]
</div>
Setting handDrawnSeed to a fixed integer ensures the diagram renders with identical jitter patterns on every page load.
Node.js API Implementation
For server-side rendering or build pipelines, configure the seed during initialization to guarantee consistent SVG output across builds:
import mermaid from 'mermaid';
mermaid.initialize({
look: 'handDrawn',
handDrawnSeed: 42,
});
const definition = `
graph LR
X --> Y
Y --> Z
`;
mermaid.render('graphDiv', definition, (svgCode) => {
console.log(svgCode);
});
The generated svgCode will contain the same Rough.js seed values on each execution, making the output suitable for snapshot testing.
Per-Node Style Overrides
Apply the hand-drawn style to specific nodes only using class definitions within the diagram syntax:
graph TD
A[Classic Style] --> B[Hand Drawn]:::hand
classDef hand look handDrawn, handDrawnSeed=99;
This configuration applies look: 'handDrawn' and a deterministic seed (99) exclusively to nodes tagged with the hand class.
Source Code Implementation Details
The handDrawn rendering path spans several key files in the mermaid-js/mermaid repository:
| File | Role |
|---|---|
packages/mermaid/src/schemas/config.schema.yaml |
Defines the look enum (classic, handDrawn) and handDrawnSeed numeric property. |
packages/mermaid/src/rendering-util/rendering-elements/shapes/handDrawnShapeStyles.ts |
Central utilities (solidStateFill, userNodeOverrides) that inject Rough.js options including the seed into node and fill styles. |
packages/mermaid/src/rendering-util/rendering-elements/edges.js |
Applies seed-driven Rough.js styling to edge paths when look: 'handDrawn' is active. |
packages/mermaid/src/diagram-api/diagramAPI.js |
Provides getConfig() used throughout the rendering pipeline to retrieve the current handDrawnSeed value. |
All shape-rendering modules guard Rough.js invocations with conditional checks against node.look, ensuring the classic rendering path remains unaffected when the handDrawn look option is disabled.
Summary
- The handDrawn look option activates Rough.js integration to render diagrams with sketch-like, randomized aesthetics instead of crisp vector geometry.
handDrawnSeedaccepts an integer that seeds Rough.js's random number generator, making the jitter patterns deterministic and reproducible across renders.- When
handDrawnSeedis0(default), Rough.js selects random values on each render, causing visual variation; any non-zero value locks the randomization. - The seed propagates through
handDrawnShapeStyles.tsandedges.jsvia the global configuration retrieved bygetConfig().
Frequently Asked Questions
What is the default value of handDrawnSeed?
The default value is 0. When set to 0, Rough.js generates a new random seed internally for every render, causing the hand-drawn diagram to appear slightly different each time it is generated.
Can I use handDrawn with any Mermaid diagram type?
The handDrawn look option is supported across most Mermaid diagram types that utilize the standard rendering pipeline, including flowcharts, sequence diagrams, and class diagrams. However, the implementation requires the specific shape files to check node.look !== 'handDrawn' and apply Rough.js styles accordingly, so compatibility depends on whether the individual diagram implementation includes these guards.
Why does my handDrawn diagram look different on every page load?
This occurs because handDrawnSeed defaults to 0, which instructs Rough.js to randomize its seed on each initialization. To freeze the appearance, set handDrawnSeed to any specific non-zero integer in your Mermaid configuration; this ensures Rough.js receives the same seed for every element on every render.
Does handDrawnSeed affect performance?
No, the handDrawnSeed value itself has negligible performance impact. However, the handDrawn look option overall can be slightly slower than classic rendering because Rough.js generates more complex SVG paths with randomized perturbations and hachure fills, increasing the DOM node count and rendering complexity compared to simple geometric shapes.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →