Make the data model use Item and List types universally, with nodes authored as rank-polymorphic kernels (#4335)

* Add rank polymorphism node audit classifying all 271 nodes

* Implement StaticType for Item<T>

* Generate Item and mapped List wire variants for nodes declaring an Item<T> primary input

* Migrate nine nodes to Item element-wise kernels, dissolving the blending trait boilerplate

* Document the Item kernel implementation and staging plan

* Route Item<Vector> through TaggedValue::TypeDefault

* Add executor integration tests covering the Item and List wire variants

* Collapse element-wise Item/List wire pairs to the List form for conversion insertion

* Migrate sixteen vector modifier nodes to Item element-wise kernels

* Migrate Sample Image, Extend Image to Bounds, and Dehaze to Item element-wise kernels

* Fix bevel_with_transform test to actually exercise the transform attribute

* Implement From<T> for Item<T>

* Register PromoteNode rank adapters wrapping bare values into Item wires

* Insert PromoteNode adapters for Item/List wire pair fields in the preprocessor

* Define a real promote node backing the PromoteNode registry identifiers

* Zip ranked Item connectors by frame slot in the mapped element-wise variant

* Register ItemToListNode singleton raise adapters

* Resolve Item wires against List connectors by inserting promotion adapters at construction

* Rank the Offset Points distance connector and prove mixed-rank resolution end-to-end

* Implement Clampable for Item and List wires with per-variant clamp bounds

* Rank the Round Corners radius connector, exercising hard bounds on a ranked wire

* Implement ApplyTransform for Item

* Add Item wire implementations to the Transform node, keeping rank-0 chains rank 0

* Detect element-wise nodes by lazy primary connectors declaring Output = Item

* Convert Transform to an Item kernel with ranked parameters, delivering the broadcast milestone

* Rename Apply Transform to Bake Transform, baking item transforms on Vector, DAffine2, and DVec2

* Promote bare wires onto Item connectors at resolution via WrapItemNode adapters

* Rank the numeric, vector, and boolean parameters across the migrated element-wise nodes

* Rank the enum, integer, and seed parameters, registering their rank adapters via a consolidated macro

* Amend the audit with the DashPattern value type resolution

* Migrate the string family to Item element-wise kernels

* Unwrap Item wires into bare legacy connectors at resolution via UnwrapItemNode adapters

* Shadow owned node parameters in bodies instead of mut in signatures

* Migrate the math family and string measure nodes to Item element-wise kernels

* Convert the comparison and clamp nodes to Item kernels, dropping unreachable &str rows

* Flat-map expander kernels returning List under the mapped variant's frame

* Migrate the expander nodes to Item kernels flat-mapping under the frame

* Remove the unused peel_list helper

* Rank the raster adjustment and blending kernels, recontextualizing shader nodes onto an Item stand-in

Migrate the 16 adjustment nodes, Mix, Color Overlay, and Gradient Map from whole-List kernels to rank-0 Item kernels, letting the macro derive the List-mapped (zip) variants. Move the Adjust and Blend per-element seams off List onto the element types (add the Raster<CPU> impls, drop the now-dead List impls).

Shader nodes keep their bodies verbatim: PerPixelAdjust re-emits the identical kernel against a transparent no_std Item stand-in, so every Item<T> connector and .element() call resolves to a zero-cost identity on the GPU while the uniform buffer stays bare repr(C). The macro peels Item off ranked uniform params, wraps the fetched texel and uniforms at the entry point, and unwraps the result. This drops the shader_node/Item incompatibility guard. Register rank adapters for the adjustment enums.

* Update the rank polymorphism roadmap for the landed shader-node and adjustments chunk

* Rename the GPU Item stand-in to ShaderItem, aliased as Item at its shader-node import sites

* Flip the vector shape generators to emit rank-0 Item<Vector>

The shape generators (Rectangle, Circle, Ellipse, Arc, Spiral, Polygon, Star, Arrow, Line, Grid, QR Code) each produced exactly one shape wrapped in a singleton List<Vector>. Emit Item<Vector> directly so they connect to the rank-0 content connector of the migrated Transform node. Downstream List consumers receive the value through the existing Item to List promotion.

Relax the element-wise validation so a `()` (generator) primary may return Item<T> without being element-wise. Adapt the Repeat on Points test, which still takes a List content connector, by raising the generator's Item output through a singleton wrapper node.

* Parse ranked Item<T> parameter defaults against the bare element type

A ranked `Item<T>` parameter's default value is a bare, unranked `T` (promoted to the wire at resolution), but the preprocessor was handed the wrapped `Item<T>` type and could not parse the literal, flooding the console with warnings and dropping the defaults. Key the field's default_type metadata off the peeled element type for concrete ranked parameters, leaving generic `Item<T>` primaries and skip_impl nodes untouched.

* Parse an element-wise primary's scalar default against the bare element type

An element-wise node's primary reports its default_type as the List wire form so an unconnected primary defaults to an empty list. But when the primary carries a scalar `#[default]` (such as Root's radicand), that literal must parse as a bare element, not a List. Key the primary's default_type off the bare element type when it has a Default value source, keeping the List form otherwise.

* Add the DashPattern value type for stroke dash sequences

Introduce a rank-0 DashPattern value type (a Vec<f64> of alternating dash and gap lengths) so a stroke's dash pattern is a single frameable value rather than a rank-1 List<f64>. Register it as an auto-generated TaggedValue variant, parse its default from a comma or space separated string, and register its rank adapters. Not yet wired into the Stroke node.

* Rank the Fill and Stroke nodes element-wise and give Stroke a DashPattern connector

Migrate Fill and Stroke to element-wise Item<V> primaries (over Vector and Graphic element types) via a new element-level VectorItemMut trait, so styling one shape yields one shape and rank is preserved instead of promoting the input to a singleton List and emitting a List. The macro derives the List-mapped variant for genuine collections.

Wire the Stroke dash sequence to the new rank-0 DashPattern value type, collapsing the old content x paint x dash cartesian and dropping the IntoF64Vec trait. Update the stroke properties dash widget, the drawing tool, and graph-operation plumbing to read and write DashPattern, and migrate legacy F64Array, F64, and String dash inputs on document open.

Assign Colors stays a whole-collection node: each element's gradient position depends on its index among all siblings, which the element frame does not expose, so it keeps its List primary and the VectorListIterMut trait.

* Register rank adapters for the ranked Stroke enum parameters

The element-wise Stroke node ranks its align, cap, and paint order parameters as Item<StrokeAlign>, Item<StrokeCap>, and Item<PaintOrder>, but those enums lacked promotion adapters, so a bare default enum value could not be promoted to its Item wire and no Stroke variant resolved ("No construct found for node"). Register their rank adapters alongside StrokeJoin.

* Display Item wires in the Data panel without a List's ID column

Add a TableItemLayout impl for Item<T> and recognize Item wire types when introspecting graph data. An Item holds a single element, so it renders as a one-row table of the element plus its attributes with no leading index column, and it labels as its element type T rather than a List's T[]. Add ItemAttributeValues::get_any for the attribute widget dispatch.

* Register MonitorNode for Item wire types so the Data panel introspects them directly

Graph introspection wraps the inspected output in a generic MonitorNode typed to the wire. Without Item<T> monitor registrations, an Item<Vector> output could only be monitored after an Item to List promotion, so the Data panel captured and displayed a List<Vector> despite the connector being Item<Vector>. Register monitors for the Item types the element-wise nodes emit, and add the matching Data panel downcast entries.

* Color and double Item/List wires and cleave layer-stack connectors in the node graph

* Route wire color and rank through hidden nodes and refresh them on type changes

* Rework the DashPattern connector conversions with element-wise promotion and an explicit reducer node

* Rank the remaining value, context, aggregation, and transform nodes onto Item<T> wires

* Back DashPattern with a List<f64> so the Data panel can introspect its lengths

* Carry a single Item<T> through varargs so the Read context nodes emit Item<T> not List<T>

* Relax rank validation for aggregation shapes, add element adapters, and match variants by fewest promotions

* Rank the remaining bare and unnecessarily-List connectors across the node catalog

* Add Graphic::None and the FillChoice paint value, making colors and gradients plain values

* Rename GradientStops to Gradient and the legacy Gradient/Fill structs to LegacyGradient/LegacyFill

* Restore generator frame-from-params ranking to the roadmap as a planned stage

* Rename the ranked-field adapter identifier from PromoteNode to FieldAdapterNode to reflect its full contract

* Unload only the wires whose displayed style changed when types update

* Peel wire rank in the editor's semantic type checks so rank-0 layers are recognized

* Restore the whole-List Transform variant so rank-1 content wires resolve again

* Register the Item wire forms for the Memoize and Context Modification infrastructure nodes

* Give every ranked connector a field adapter and add numeric cast variants for legacy wires

* Key a ranked param's type default off its Item wire form when no literal default exists

* Inherit the layer's content value when splicing a node into an empty chain

* Migrate stale List-form TypeDefault inputs to the definition's current default

* Generate the mapped wire variant only when the element-wise node has a frame source

* Let a bare wire feed a List connector via a wrap-raise adapter, costed as two rank steps

* Add a zip companion to the whole-List Transform so ranked List parameters pair per slot

* Add the Sum, Average, Minimum, Maximum, Any, and All list reducers

* Convert the measure family to element-wise Item kernels per the audit classification

* Prefer the bare element value over the Item type default so ranked params keep their widgets

* Rename GradientStopsUI to GradientUI

* Split Fill's optional transform into a _has_transform bool and a ranked _transform matrix

* Rename the migration-only OptionalDAffine2 TaggedValue to LegacyOptionalDAffine2

* Flow byte buffers as Item<Resource> instead of List<u8> across the byte nodes

* Macro-generate the list-content wire variant, retiring the hand-written Transform-zip, Area, and Centroid companions

* Let ()-primary generators take ranked params and frame over them via the mapped variant, ranking Circle's radius

* Rank the vector shape generators' params to Item, adding a rank-aware input grab to the introspection harness

* Rank the value, color, and text generator params to Item

* Rank the raster, web-request, and context-reader generator params to Item

* Fix the repeat and brush test wirings left behind by the param-ranking sweeps

* Delete the vestigial Some, Unwrap Option, and Size Of debug nodes

* Delete the Attach Attribute node, folding its role into Write Attribute

* Add the Filter and Sort list companion nodes

* Guard the removed-definition migration swap target with a test

* Add the Box Corners value type in place of the rectangle corner radius list

* Split Text to Vector's per-glyph mode into a Text to Vector Glyphs node

* Rank the Combine Channels node's channel connectors to Item

* Make Map Points an element-wise node

* Delete the deprecated Upload Texture node

* Update the implementation roadmap to reflect the landed stages

* Let monitor introspection read rank-0 wires, locking in the layer coercion promotion path

* Prefer the rank-0 default when disconnecting a rank-capable input

* Make Path Modify an element-wise node

* Wrap node paths in a NodeIdPath newtype so they flow as a single Item

* Give Item<Raster<CPU>> a default so an unconnected Brush background resolves

* Stop the Brush node from setting layer attributes its paint operation doesn't produce

* Present-gate Flatten Path's adopted layer path like its fill and stroke

* Gate carried layer attributes on static column presence, not runtime values

* Give the remaining graphic Item<T> types a default so unconnected primaries resolve

* Dispatch a ranked param's Properties widget from its rank-0 element type

* Make Extract Transform an element-wise node, restoring the Origins to Polyline body

* Rename Flatten Path to Combine Paths

* Stamp Legacy Layer Extend's adopted layer path as a readable NodeIdPath

* Drop the dead List<u8> and List<NodeId> wire rows

* Rank Flatten Graphic's Fully Flatten toggle to Item

* Update the implementation roadmap with the endgame scope

* Make Combine Paths a reducer that collapses the whole frame into one path

* Stop type-converter nodes from carrying the source's unrelated attributes

* Format the Origins to Polyline regression test

* Wrap the Brush node's trace in a BrushTrace newtype so it flows as one value

* Make Switch a framed element-wise select, bundling whole collections

* Widen and align element-type coverage across the list and graphic nodes

* Register the compiler's cache chain pair for every ranked enum and newtype wire

* Fix wire colors for Passthrough outputs, bundled lists, and bools, and widen list wires

* Represent List wire types structurally with Type::List, replacing name-parsed rank promotion

* Treat scope and data fields as environment, rank scope wires as Item, and feed the render boundary through a context vararg

* Delete the vestigial Clone debug node

* Reinstate Upload Texture as an element-wise node and fix the GPU variants' scope executor and rank adapters

* Rename Combine Paths back to Flatten Path, deferring that rename to its own PR

* Deduplicate the promotion adapter registrations into the field adapter macro

* Rank Write Attribute's value connector to Item<AttributeValueDyn>, retiring the UnwrapItem bridge

* Vertical wire styling

* Store the editor layer path attribute as a bare NodeIdPath, not an Item<NodeIdPath>

* Rank Context Modification's features connector to Item<ContextFeatures>, dropping the dead memoize row

* Rank Path Modify's modification parameter to Item<Box<VectorModification>>

* Rename the field adapter node family to input adapter

* Drop the dead bare scalar rows from Context Modification's implementations list

* Move the dynamic executor's test module into its own file

* Drop the registry's unreachable bare rows for Memoize, the cache chain, and ConvertNode

* Materialize stored TaggedValues as ranked Item wires at the source

* Remove the bare-wire promotion and adapter machinery made dead by ranked value materialization

* Plant the input adapter for List-only inputs, composing position conversion from standard rows

* Consolidate Into/Convert conversions into the input adapter umbrella and rename the rank adapter identifiers

* Fix grouped layers gaining a phantom None stack element from the FillChoice default hijacking every List<Graphic> disconnect

* Enforce ranked node inputs in the macro, rejecting bare wire declarations

* Remove the unit Context => () machinery rows, leaving () purely as the no-primary sentinel

* Add a --signatures rank-audit mode to node-docs for the ranked-wire migration

* Remove the node-docs --signatures rank-audit mode now that ranked wires are enforced

* Migrate legacy no-color values on the Black & White, Color Overlay, and Empty Image color inputs

* Rewrite the element-wise accessor wire type at the primary input, not raw index 0

* Register the cache chain for Resource wires, replacing the lone hand-written Monitor row

* Gate the remaining Raster<GPU> registry rows behind the gpu feature

* Let List<DVec2> wires erase to ListDyn for the attribute reader and element counter

* Rename Extract Element to Item at Index, Count Elements to List Length, and Omit Element to Remove at Index

* Store paint picks as plain color/gradient values, removing the FillChoice value type

* Code review restructuring

* Sort by the consumed sort_key attribute or natural element order, adding the Sort Key node

* Remove the new list-combinator and reducer nodes to defer them to a follow-up PR

* Parse Fill and Stroke color defaults through the paint wire's Graphic element

* Emit ranked implementation-row default types structurally so their element TypeIds survive to default-literal parsing

* Exempt the deliberate no-paint choice from the stale List-form TypeDefault migration

* Migrate the legacy 4-input Fill directly to the split has-transform shape

* Upgrade the demo artwork

* Fix the valid AI review findings: Item eq/hash contract, table-era no-paint migration, quantize List rows, and other smaller issues

* Remove the rank polymorphism working documents

* Hash Item attribute values directly instead of debug-formatting them, speeding up cached evaluation

* Replace the data panel's dead bare-wire downcast arms with full coverage of the ranked monitor row types

* Derive PartialEq for Item now that attributes participate in equality

* Extend the data panel's attribute dispatchers with the newly supported scalar and choice enum types

* Add List monitor rows for the framed numeric conversion outputs so inspecting them resolves, with matching data panel arms
This commit is contained in:
Keavon Chambers
2026-09-08 16:03:01 +00:00
committed by Timon
parent 296185b7fc
commit a708a54492
3257 changed files with 766342 additions and 1829 deletions
@@ -0,0 +1,525 @@
+++
title = "Free online vector editor & procedural design tool"
template = "section.html"
[extra]
css = ["/page/index.css", "/component/carousel.css", "/component/feature-icons.css", "/component/feature-box.css", "/component/youtube-embed.css"]
js = ["/js/component/carousel.js", "/js/component/youtube-embed.js", "/js/component/video-autoplay.js"]
linked_js = []
meta_description = "Open source free software. A vector graphics creativity suite with a clean, intuitive interface. Opens instantly (no signup) and runs locally in a browser. Exports SVG, PNG, JPG."
+++
<!-- replacements::text_balancer() -->
<!-- ▛ LOGO ▜ -->
<section id="logo">
<div class="block">
<img src="https://static.graphite.art/logos/graphite-logotype-color.svg" alt="Graphite Logo" />
</div>
</section>
<!-- ▙ LOGO ▟ -->
<!-- ▛ TAGLINE ▜ -->
<section id="tagline">
<div class="block">
<h1 class="balance-text">Your <span>procedural</span> toolbox for 2D content creation</h1>
<p class="balance-text">Graphite is a free, open source vector graphics editor and animation engine, available now in alpha. Get creative with a fully nondestructive editing workflow that combines layer-based compositing with node-based generative design.</p>
</div>
</section>
<!-- ▙ TAGLINE ▟ -->
<!-- -->
<!-- ▛ QUICK LINKS ▜ -->
<section id="quick-links" data-quick-links>
<div class="call-to-action-buttons">
<a href="https://github.com/GraphiteEditor/Graphite" class="button github-stars">
<img src="https://static.graphite.art/icons/github.svg" alt="GitHub" />
<span class="arrow">Star</span>
<div data-github-stars></div>
</a>
<a href="#newsletter" class="button arrow">Subscribe to newsletter</a>
</div>
<div class="social-media-buttons">
<a href="https://discord.graphite.art" target="_blank">
<img src="https://static.graphite.art/icons/discord__2.svg" alt="Discord" />
</a>
<a href="https://www.reddit.com/r/graphite/" target="_blank">
<img src="https://static.graphite.art/icons/reddit__3.svg" alt="Reddit" />
</a>
<a href="https://bsky.app/profile/graphiteeditor.bsky.social" target="_blank">
<img src="https://static.graphite.art/icons/bluesky.svg" alt="Bluesky" />
</a>
<a href="https://twitter.com/graphiteeditor" target="_blank">
<img src="https://static.graphite.art/icons/twitter.svg" alt="Twitter" />
</a>
<a href="https://www.youtube.com/@GraphiteEditor" target="_blank">
<img src="https://static.graphite.art/icons/youtube.svg" alt="YouTube" />
</a>
</div>
</section>
<script>
(async () => {
const element = document.querySelector("[data-github-stars]");
try {
const response = await fetch("https://api.github.com/repos/graphiteeditor/graphite?per_page=1");
const json = await response.json();
const stars = parseInt(json.stargazers_count);
if (!stars) throw new Error();
let quantity = stars.toLocaleString("en-US");
element.innerText = quantity;
// Force repaint to work around Safari bug <https://bugs.webkit.org/show_bug.cgi?id=286403> (remove this and its data attribute when the bug is fixed and widely deployed)
document.querySelector("[data-quick-links]").style.transform = "scale(1)";
} catch {
element.remove();
}
})();
</script>
<!-- ▙ QUICK LINKS ▟ -->
<!-- ▛ SCREENSHOTS ▜ -->
<section id="screenshots" class="carousel window-size-1" data-carousel data-carousel-jostle-hint>
<div class="carousel-slide" data-carousel-slide>
<img src="https://static.graphite.art/content/index/gui-demo-fractal__4.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="" style="transform: translateX(-100%)" data-carousel-image />
<!-- Above is a copy of the last -->
<img onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="" style="transform: translateX(-100%)" data-carousel-image width="1920" height="1080" loading="lazy" src="https://static.graphite.art/content/index/gui-demo-painted-dreams__4.avif" />
<img onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="" style="transform: translateX(-100%)" data-carousel-image width="1920" height="1080" loading="lazy" src="https://static.graphite.art/content/index/gui-demo-magazine-page-layout.avif" />
<video style="transform: translateX(-100%)" data-carousel-image width="1920" height="1080" loop muted disablepictureinpicture disableremoteplayback preload="none" poster="https://static.graphite.art/content/index/gui-demo-animation-twirl-poster.avif"><source src="https://static.graphite.art/content/index/gui-demo-animation-twirl.webm" type="video/webm" /><source src="https://static.graphite.art/content/index/gui-demo-animation-twirl.mp4" type="video/mp4" /></video>
<video style="transform: translateX(-100%)" data-carousel-image width="1920" height="1080" loop muted disablepictureinpicture disableremoteplayback preload="none" poster="https://static.graphite.art/content/index/gui-demo-creative-coding-poster.avif"><source src="https://static.graphite.art/content/index/gui-demo-creative-coding.webm" type="video/webm" /><source src="https://static.graphite.art/content/index/gui-demo-creative-coding.mp4" type="video/mp4" /></video>
<img onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="" style="transform: translateX(-100%)" data-carousel-image width="1920" height="1080" loading="lazy" src="https://static.graphite.art/content/index/gui-demo-fractal__4.avif" />
<!-- Below is a copy of the first -->
<img src="https://static.graphite.art/content/index/gui-demo-painted-dreams__4.png" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="" style="transform: translateX(-100%)" data-carousel-image />
</div>
<div class="carousel-slide torn left" data-carousel-slide-torn-left></div>
<div class="carousel-slide torn right" data-carousel-slide-torn-right></div>
<div class="screenshot-details">
<div class="carousel-controls">
<button class="direction prev" data-carousel-prev aria-label="Move to previous screenshot">
<svg width="40" height="40" viewBox="0 0 40 40" xmlns="http://www.w3.org/2000/svg">
<path d="M20,0C8.95,0,0,8.95,0,20c0,11.05,8.95,20,20,20c11.05,0,20-8.95,20-20C40,8.95,31.05,0,20,0z M20,38c-9.93,0-18-8.07-18-18S10.07,2,20,2s18,8.07,18,18S29.93,38,20,38z" />
<polygon points="24.71,10.71 23.29,9.29 12.59,20 23.29,30.71 24.71,29.29 15.41,20" />
</svg>
</button>
<button class="dot active" data-carousel-dot aria-label="Move to screenshot 1"></button>
<button class="dot" data-carousel-dot aria-label="Move to screenshot 2"></button>
<button class="dot" data-carousel-dot aria-label="Move to screenshot 3"></button>
<button class="dot" data-carousel-dot aria-label="Move to screenshot 4"></button>
<button class="dot" data-carousel-dot aria-label="Move to screenshot 5"></button>
<button class="direction next" data-carousel-next aria-label="Move to next screenshot">
<svg width="40" height="40" viewBox="0 0 40 40" xmlns="http://www.w3.org/2000/svg">
<path d="M20,0C8.95,0,0,8.95,0,20c0,11.05,8.95,20,20,20c11.05,0,20-8.95,20-20C40,8.95,31.05,0,20,0z M20,38c-9.93,0-18-8.07-18-18S10.07,2,20,2s18,8.07,18,18S29.93,38,20,38z" />
<polygon points="16.71,9.29 15.29,10.71 24.59,20 15.29,29.29 16.71,30.71 27.41,20" />
</svg>
</button>
</div>
<div class="screenshot-description">
<p data-carousel-description class="active">
<a href="https://editor.graphite.art/#demo/painted-dreams"><em>Painted Dreams</em></a> — Made using nondestructive boolean operations and procedural polka dot patterns
</p>
<p data-carousel-description>
Design for a magazine spread, a preview of the upcoming focus on desktop publishing
</p>
<p data-carousel-description>
Procedurally generated animation demonstrating Graphite's approach to creative coding with nodes
</p>
<p data-carousel-description>
Procedurally generated animation demonstrating Graphite's approach to creative coding with nodes
</p>
<p data-carousel-description>
Mandelbrot fractal filled with a noise pattern, procedurally generated and infinitely scalable
</p>
</div>
</div>
</section>
<!-- ▙ SCREENSHOTS ▟ -->
<!-- -->
<!-- ▛ WHAT'S NEW ▜ -->
<section id="what-is-new" class="block">
<div class="block">
## What's new?
The latest major update is out now! See what the team has been cooking up recently:
<div class="block video-container">
<div>
{{ youtube_embed(id="U3E-sWo2H_M", title="May Update - Graphite, the Open Source 2D Graphics Suite") }}
</div>
</div>
</div>
</section>
<!-- ▙ WHAT'S NEW ▟ -->
<!-- -->
<!-- ▛ OVERVIEW ▜ -->
<section id="overview" class="feature-box-outer">
<div class="feature-box-inner">
<div class="block">
<h1 class="feature-box-header">Software overview</h1>
---
<div class="diptych sizzle-video">
<div class="block text">
Starting life as a vector editor, Graphite is evolving into a general-purpose, all-in-one graphics toolbox that is built more like a game engine than a conventional creative app. The editor's tools wrap its node graph core, exposing user-friendly workflows for vector, raster, animation, and beyond.
<a href="https://editor.graphite.art" class="button arrow">Start creating</a>
</div>
<div class="block video">
<video loop muted playsinline disablepictureinpicture disableremoteplayback data-auto-play preload="none" poster="https://static.graphite.art/content/index/sizzle-compilation-poster.avif">
<source src="https://static.graphite.art/content/index/sizzle-compilation.webm" type="video/webm" />
<source src="https://static.graphite.art/content/index/sizzle-compilation.mp4" type="video/mp4" />
</video>
</div>
</div>
</div>
<div class="block workflows">
## One app to rule them all
Stop jumping between programs. Upcoming tools will make Graphite a first-class content creation suite for many workflows, including:
<div class="feature-icons stacked no-background">
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 12" src="https://static.graphite.art/icons/icon-atlas-features__2.png" alt="" />
<span>Graphic Design</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 17" src="https://static.graphite.art/icons/icon-atlas-features__2.png" alt="" />
<span>Motion Graphics</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 13" src="https://static.graphite.art/icons/icon-atlas-features__2.png" alt="" />
<span>Image Editing</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 14" src="https://static.graphite.art/icons/icon-atlas-features__2.png" alt="" />
<span>Digital Painting</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 15" src="https://static.graphite.art/icons/icon-atlas-features__2.png" alt="" />
<span>Page Layout & Print</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 16" src="https://static.graphite.art/icons/icon-atlas-features__2.png" alt="" />
<span>VFX Compositing</span>
</div>
</div>
</div>
<div class="diptych">
<div class="block">
## Current features
<div class="feature-icons">
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 0" src="https://static.graphite.art/icons/icon-atlas-features__2.png" alt="" />
<span>Vector editing tools</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 10" src="https://static.graphite.art/icons/icon-atlas-features__2.png" alt="" />
<span>Procedural workflow for graphic design</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 8" src="https://static.graphite.art/icons/icon-atlas-features__2.png" alt="" />
<span>Node-based layers</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 3" src="https://static.graphite.art/icons/icon-atlas-features__2.png" alt="" />
<span>Forever free and open source</span>
</div>
</div>
Presently, Graphite is a lightweight offline web app with features primarily oriented around procedural vector graphics editing.
</div>
<div class="block">
## Upcoming features
<div class="feature-icons">
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 4" src="https://static.graphite.art/icons/icon-atlas-features__2.png" alt="" />
<span>All-in-one creative tool for all things 2D</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 5" src="https://static.graphite.art/icons/icon-atlas-features__2.png" alt="" />
<span>Fully-featured raster manipulation</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 7" src="https://static.graphite.art/icons/icon-atlas-features__2.png" alt="" />
<span>Windows/Mac/Linux native apps + web</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 6" src="https://static.graphite.art/icons/icon-atlas-features__2.png" alt="" />
<span>Live collaborative editing</span>
</div>
</div>
<a href="/features#roadmap" class="button arrow">Roadmap</a>
</div>
</div>
<div class="block">
## Desktop-first and web-ready
Where's the download? The [web app](https://editor.graphite.art) is currently live and desktop apps for Windows, Mac, and Linux are now in a release candidate stage. Check the `#🔥announcements` channel of [the Discord](https://discord.graphite.art) for download links if you would like to partake in testing prior to the impending launch.
Graphite is designed principally as a professional desktop application that is also accessible in a browser for quick access from anywhere. It's built for speed with (nearly) no JavaScript. And regardless of platform, it runs locally and privately on your own hardware— there is no server.
<a href="https://github.com/GraphiteEditor/Graphite/issues/2535" target="_blank">Engineering the tech</a> for a native app distributed across three new platforms takes extra time. That's why supporting the web platform, which keeps up-to-date and reaches all devices, has been the initial target. For now, you can <a href="https://support.google.com/chrome/answer/9658361" target="_blank">install the app as a PWA</a> for a desktop-like experience.
Graphite's code architecture is structured to deliver true native performance for your graphically intensive workloads on desktop platforms and very low overhead on the web thanks to WebAssembly and WebGPU, new high-performance browser technologies.
</div>
</div>
</section>
<!-- ▙ OVERVIEW ▟ -->
<!-- -->
<!-- ▛ DONATE ▜ -->
<section id="donate" class="block">
<div class="block">
<h2 class="heart">Support the mission</h2>
Free software doesn't grow on trees! Chip in your share of the (very real) development costs so you're not leaving others to pick up your tab. In just a few clicks, becoming a member (or giving a one-time donation) lets you help maintain Graphite's sustainability and independence.
<a href="/donate" class="button arrow">Donate now</a>
</div>
</section>
<!-- ▙ DONATE ▟ -->
<!-- -->
<!-- ▛ PROCEDURALISM ▜ -->
<section id="proceduralism" class="feature-box-outer">
<div class="feature-box-inner">
<div class="block">
<h1 class="feature-box-header">The power of proceduralism</h1>
---
Graphite is the first and only comprehensive graphic design suite built for procedural editing — where everything you make is nondestructive.
</div>
<div class="diptych red-dress">
<div class="block video-background">
<video loop muted playsinline disablepictureinpicture disableremoteplayback data-auto-play preload="none" poster="https://static.graphite.art/content/index/procedural-demo-red-dress-poster.avif">
<source src="https://static.graphite.art/content/index/procedural-demo-red-dress.webm" type="video/webm" />
<source src="https://static.graphite.art/content/index/procedural-demo-red-dress.mp4" type="video/mp4" />
</video>
</div>
<div class="block description">
<h1 class="feature-box-header balance-text">Explore parametric possibilities</h1>
Save hours on tedious alterations and make better creative choices. Graphite lets you iterate rapidly by adjusting node parameters instead of individual elements.
Scatter circles with just a couple nodes...
Want them denser? Bigger? Those are sliders.
Want a different placement area? Just tweak the path.
<a href="https://editor.graphite.art/#demo/red-dress">Open this artwork</a> and give it a try yourself.
</div>
</div>
<div class="diptych leaves">
<div class="block description">
<h1 class="feature-box-header balance-text">Mix and morph anything</h1>
Nondestructive editing means every decision is tied to a parameter you can adjust later on. Use Graphite to interpolate between any states just by dragging value sliders.
Blend across color schemes. Morph shapes before they're scattered around the canvas. The options are endless.
<a href="https://editor.graphite.art/#demo/changing-seasons">Open this artwork</a> and give it a try yourself.
</div>
<div class="block video-background">
<video loop muted playsinline disablepictureinpicture disableremoteplayback data-auto-play preload="none" poster="https://static.graphite.art/content/index/procedural-demo-leaves-poster.avif">
<source src="https://static.graphite.art/content/index/procedural-demo-leaves.webm" type="video/webm" />
<source src="https://static.graphite.art/content/index/procedural-demo-leaves.mp4" type="video/mp4" />
</video>
</div>
</div>
<div class="block pipelines">
## Geared for generative pipelines
Graphite's representation of artwork as a node graph lets you customize, compose, automate, reuse, and share your content workflows.
<div class="feature-icons four-wide">
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 9" src="https://static.graphite.art/icons/icon-atlas-features__2.png" alt="" />
<span class="balance-text">Infinitely pan and zoom without pixelation, export any resolution</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 1" src="https://static.graphite.art/icons/icon-atlas-features__2.png" alt="" />
<span class="balance-text">Compile standalone programs from node systems <em>(future)</em></span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 11" src="https://static.graphite.art/icons/icon-atlas-features__2.png" alt="" />
<span class="balance-text">Deploy asset pipelines in studio production environments <em>(future)</em></span>
</div>
</div>
</div>
</div>
</section>
<!-- ▙ PROCEDURALISM ▟ -->
<!-- -->
<!-- ▛ NEWSLETTER ▜ -->
<section id="newsletter" class="feature-box-narrow">
<div id="newsletter-success"><!-- Used only as a URL hash fragment anchor --></div>
<div class="diptych">
<div class="block newsletter-signup">
<h1 class="feature-box-header">Stay in the loop</h1>
Subscribe to the newsletter for future updates on major development progress. And follow along, or join the conversation, on social media.
<div class="newsletter-success">
## Thanks!
You'll receive your first newsletter email with the next major Graphite news.
</div>
<form action="https://graphite.art/newsletter-signup" method="post">
<div class="same-line">
<div class="input-column name">
<label for="newsletter-name">First + last name:</label>
<input id="newsletter-name" name="name" type="text" required />
</div>
<div class="input-column phone">
<label for="newsletter-phone">Phone:</label>
<input id="newsletter-phone" name="phone" type="text" tabindex="-1" autocomplete="off" />
</div>
<div class="input-column email">
<label for="newsletter-email">Email address:</label>
<input id="newsletter-email" name="email" type="email" required />
</div>
</div>
<div class="input-column submit">
<input type="submit" value="Subscribe" class="button" />
</div>
</form>
</div>
<div class="block social-media-links">
<a href="https://discord.graphite.art" target="_blank">
<img src="https://static.graphite.art/icons/discord__2.svg" alt="" />
<span class="link not-uppercase arrow">Discord</span>
</a>
<a href="https://www.reddit.com/r/graphite/" target="_blank">
<img src="https://static.graphite.art/icons/reddit__3.svg" alt="" />
<span class="link not-uppercase arrow">Reddit</span>
</a>
<a href="https://bsky.app/profile/graphiteeditor.bsky.social" target="_blank">
<img src="https://static.graphite.art/icons/bluesky.svg" alt="" />
<span class="link not-uppercase arrow">Bluesky</span>
</a>
<a href="https://twitter.com/graphiteeditor" target="_blank">
<img src="https://static.graphite.art/icons/twitter.svg" alt="" />
<span class="link not-uppercase arrow">Twitter</span>
</a>
<a href="https://www.youtube.com/@GraphiteEditor" target="_blank">
<img src="https://static.graphite.art/icons/youtube.svg" alt="" />
<span class="link not-uppercase arrow">YouTube</span>
</a>
</div>
</div>
</section>
<!-- ▙ NEWSLETTER ▟ -->
<!-- -->
<!-- ▛ DIVE IN ▜ -->
<section id="dive-in" class="block">
<div class="block">
## Ready to dive in?
Get started with Graphite by following along to a hands-on quickstart tutorial.
<div class="block video-container">
<div>
{{ youtube_embed(id="7gjUhl_3X10", title="Vector Art Quickstart - Graphite, the Open Source 2D Graphics Suite") }}
</div>
</div>
<div class="buttons">
<a href="https://editor.graphite.art" class="button arrow">Launch Graphite</a>
<a href="/learn" class="button arrow">Continue learning</a>
</div>
</div>
</section>
<!-- ▙ DIVE IN ▟ -->
<!-- -->
<!-- ▛ RECENT NEWS ▜ -->
<section id="recent-news" class="feature-box-outer">
<div class="feature-box-inner">
<h1 class="feature-box-header">Recent news <span> / </span> <a href="/blog" class="link arrow">More in the blog</a></h1>
---
<div class="diptych">
<!-- replacements::blog_posts(count = 2) -->
</div>
</div>
</section>
<!-- ▙ RECENT NEWS ▟ -->
+184
View File
@@ -0,0 +1,184 @@
+++
title = "About Graphite"
[extra]
css = ["/page/about.css", "/component/feature-box.css"]
+++
<section>
<div class="block">
# About Graphite
Graphite is a community-built free software project. If you find it valuable, consider [donating](/donate) or [getting involved](/volunteer) to keep it sustainable.
</div>
<div class="block">
## Project
The idea for Graphite began with a desire to create artwork and edit photos using free software that felt user-friendly and truly modern. Over time, that dream evolved to reconsider what "modern" meant for the landscape of 2D graphics editing. By borrowing concepts popular in 3D software, what could a procedural, nondestructive design tool look like if nothing was too ambitious? Answering that question took years of design exploration, leading to a community of savvy developers volunteering to turn that formidable dream into a reality.
</div>
<div class="diptych">
<div class="block">
## Mission
Graphite strives to unshackle the creativity of every budding artist and seasoned professional by building the best comprehensive art and design tool that's accessible to all.
Mission success will come when Graphite is an industry standard. A cohesive product vision and a focus on innovation over imitation is the strategy that will make that possible.
</div>
<div class="block">
## Organization
Graphite is fully funded by its community and beholden to no investors, ensuring it remains free and open source forever. The organization, *Graphite Labs, LLC*, is owned and controlled solely by the project's founder. Future nonprofit foundation status is being explored, but that administrative complexity is not yet justified. All income is being reinvested into the project, with aims to employ full-time developers once funding reaches sustainable levels.
</div>
</div>
</section>
<!-- ## Statistics
- [GitHub stars](https://github.com/GraphiteEditor/Graphite/stargazers): <span class="loading-data" data-github-stars></span>
- [Contributors](https://github.com/GraphiteEditor/Graphite/graphs/contributors): <span class="loading-data" data-contributors></span>
- [Code commits](https://github.com/GraphiteEditor/Graphite/commits/master): <span class="loading-data" data-code-commits></span>
- [First line of code](https://github.com/GraphiteEditor/Graphite/commit/bca97cbeff8e38b426cfb410159cb21132062fba): Feb. 14, 2021
<script>
(async () => {
const response = await fetch("https://api.github.com/repos/graphiteeditor/graphite?per_page=1");
const json = await response.json();
const stars = parseInt(json.stargazers_count);
if (!stars) return;
document.querySelector("[data-github-stars]").innerText = `${Math.round(stars / 100) / 10}k ⭐`;
})();
(async () => {
const response = await fetch("https://api.github.com/repos/graphiteeditor/graphite/contributors?per_page=1");
const link = [...response.headers].find(([header, _]) => header === "link")[1];
if (!link) return;
// With one page per contributor, the last past number is the contributor count
const contributors = parseInt(link.match(/page=(\d+)>; rel="last"/)[1]);
if (!contributors) return;
document.querySelector("[data-contributors]").innerText = contributors;
})();
(async () => {
const response = await fetch("https://api.github.com/repos/graphiteeditor/graphite/commits?per_page=1");
const link = [...response.headers].find(([header, _]) => header === "link")[1];
if (!link) return;
// With one page per commit, the last past number is the commit count
const commits = parseInt(link.match(/page=(\d+)>; rel="last"/)[1]);
if (!commits) return;
document.querySelector("[data-code-commits]").innerText = commits;
})();
</script> -->
<!-- <section id="opener-message">
<div class="block">
## A 2D creative tool made for everyone
With great power comes great accessibility. Graphite is built on the belief that the best creative tools can be powerful and within reach of all, from students to studios.
Graphite is designed with a friendly and intuitive interface where a delightful user experience is of first-class importance. It is available for free under an open source [license](/license) and usable [instantly through a web browser](https://editor.graphite.art) or an upcoming native client on Windows, Mac, and Linux.
It's easy to learn and teach, yet Graphite's accessible design does not sacrifice versatility for simplicity. The node-based workflow opens doors to an ecosystem of powerful capabilities catering to casual and professional users alike.
</div>
<div class="graphic">
<img src="https://static.graphite.art/content/index/brush__2.svg" alt="" />
</div>
</section> -->
<section id="core-team" class="feature-box-outer">
<div class="feature-box-inner">
<h1 class="feature-box-header">Meet the core team</h1>
---
<div class="triptych">
<div class="block" id="keavon">
<img src="https://static.graphite.art/content/about/core-team-photo-keavon-chambers.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Photo of Keavon Chambers" />
## Keavon Chambers<br /><span class="handle">(@Keavon)</span> <img src="https://static.graphite.art/icons/flags/us.png" class="flag" title="American" />
***Founder, UI & product design, frontend, editor systems***
Keavon is a creative generalist with a love for the fusion of arts and technology. UX and graphic designer, photographer, game developer, technical artist, and everything in between— he is equal parts designer and engineer. His multidisciplinary background in the digital arts is aptly suited for concocting the unique vision needed to bring Graphite to fruition.
</div>
<div class="block" id="dennis">
<img src="https://static.graphite.art/content/about/core-team-photo-dennis-kobert.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Photo of Dennis Kobert" />
## Dennis Kobert<br /><span class="handle">(@TrueDoctor)</span> <img src="https://static.graphite.art/icons/flags/de.png" class="flag" title="German" />
***Graphene node engine, research, architecture***
Dennis is a mix between a mathematician and a mad scientist. While still enjoying the art of photography and image editing (which drew him to the project early on), he thrives when challenged with designing complex systems and pushing boundaries. His method of building generalized solutions wrapped in elegant layers of abstraction led to his creation of the Graphene engine.
</div>
<div class="block" id="timon">
<img src="https://static.graphite.art/content/about/core-team-photo-timon-schelling.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Photo of Timon Schelling" />
## Timon Schelling<br /><span class="handle">(@timon-schelling)</span> <img src="https://static.graphite.art/icons/flags/de.png" class="flag" title="German" />
***Desktop application engineering***
Timon believes open source tools are essential to grant digital artists freedom in access, creativity, and expression. He builds and maintains Graphite's native desktop app, ensuring its polish, reliability, and cross-platform consistency. Drawn to Graphite by its vision of procedural, nondestructive, expansive 2D art tools, he works to make that vision usable by as many people as possible.
</div>
</div>
</div>
</section>
<section>
<div class="triptych" id="extras">
<div class="block">
## Credits
In addition to the work of the Core Team listed above, over a hundred other contributors have written code that makes Graphite what it is today:
<a href="https://github.com/GraphiteEditor/Graphite/graphs/contributors" class="button arrow">Credits</a>
</div>
<div class="block">
## License
The Graphite editor source code is published under the terms of the Apache License 2.0. See below for details and exclusions:
<a href="/license" class="button arrow">License</a>
</div>
<div class="block">
## Logo
More information about the Graphite logo, including its community-focused usage policy and downloadable assets, is available on the logo page:
<a href="/logo" class="button arrow">Logo</a>
</div>
</div>
</section>
@@ -0,0 +1,22 @@
+++
title = "Announcing Graphite alpha"
date = 2022-02-12
[extra]
banner = "https://static.graphite.art/content/blog/2022-02-12-announcing-graphite-alpha.avif"
banner_png = "https://static.graphite.art/content/blog/2022-02-12-announcing-graphite-alpha.png"
author = "Keavon Chambers"
summary = "The Graphite open source team announces the alpha release of their next-generation graphics editor, a web-based SVG editor with vector-based tools. Future plans include a node-based procedural workflow, a raster graphics compositing engine, and a native desktop client."
reddit = "https://www.reddit.com/r/graphite/comments/unw3hi/blog_post_announcing_graphite_alpha/"
twitter = "https://twitter.com/GraphiteEditor/status/1524663930697568256"
+++
The Graphite open source team is proud to announce the release of the alpha version of our next-generation graphics editor. This is the culmination of one year of development (and 1000 GitHub stars) by our passionate and dedicated community. The alpha release, a minimum viable product vector editor web app, completes the first chapter for our project. We can't wait to now forge ahead towards additional milestones and a beta release as we build a node-based procedural workflow, a raster graphics compositing engine, a native desktop client, and further ambitions.
<!-- more -->
We hope that Graphite can serve the initial use case of a lightweight web-based SVG editor. SVG export and source document saving is supported but editable SVG import will need further work. All the vector and general tools (blue and gray tool icons, respectively) are now available in some form, while raster tools (orange icons, grayed out) are forthcoming.
While in alpha, file format stability cannot be guaranteed so you may need to finish your saved documents in an older version from near the date it was started. To access an older version, ask a team member [on Discord](https://discord.graphite.art) to locate an editor build link matching your document's creation date.
Your feedback and bug reports will be valuable in helping the team prioritize improvements to ensure Graphite can be as useful as possible for artists during the alpha phase. Please go [make some art](https://editor.graphite.art) to help inform the development process. Thank you for joining us on this exciting journey.
@@ -0,0 +1,22 @@
+++
title = "Graphite: a vision for the future of 2D content creation"
date = 2022-03-12
[extra]
banner = "https://static.graphite.art/content/blog/2022-03-12-graphite-a-vision-for-the-future-of-2d-content-creation.avif"
banner_png = "https://static.graphite.art/content/blog/2022-03-12-graphite-a-vision-for-the-future-of-2d-content-creation.png"
author = "Keavon Chambers"
summary = "Graphite is an open-source application for 2D graphics editing and digital content creation, offering a nondestructive, node-based workflow. It combines intuitive UI with powerful procedural image generators to revolutionize 2D content creation."
reddit = "https://www.reddit.com/r/graphite/comments/unw3va/blog_post_graphite_a_vision_for_the_future_of_2d/"
twitter = "https://twitter.com/GraphiteEditor/status/1524664010091556864"
+++
Graphite is an open source, cross-platform digital content creation desktop and web application for 2D graphics editing, photo processing, vector art, digital painting, illustration, data visualization, compositing, and more. Inspired by the open source success of Blender in the 3D domain, it aims to bring 2D content creation to new heights with efficient workflows influenced by Photoshop/Gimp and Illustrator/Inkscape and backed by a powerful node-based, nondestructive approach proven by Houdini, Nuke, Blender, and others.
The user experience of Graphite is of central importance, offering a meticulously-designed UI catering towards an intuitive and efficient artistic process. Users may draw and edit in the traditional interactive (WYSIWYG) viewport with the Layers panel or jump in or out of the node graph at any time to tweak previous work and construct powerful procedural image generators that seamlessly sync with the interactive viewport. A core principle of the application is its 100% nondestructive workflow that is resolution-independent, meaning that raster-style image editing can be infinitely zoomed and scaled to arbitrary resolutions at a later time because editing is done by recording brush strokes, vector shapes, and other manipulations parametrically.
<!-- more -->
One might use the painting tools on a small laptop display, zoom into specific areas to add detail to finish the artwork, then perhaps try changing the simulated brush style from a blunt pencil to a soft acrylic paintbrush after-the-fact, and finally export the complete drawing at ultra high resolution for printing on a large poster.
On the surface, Graphite is an artistic medium for drawing anything imaginable. Under the hood, Graphite's node graph engine powers procedural graphics processing to produce unique artwork and automated data-driven visualizations. Graphite unlocks your creative potential in a familiar, free, accessible, and lightweight package.
@@ -0,0 +1,84 @@
+++
title = "Distributed computing in the Graphene runtime"
date = 2022-05-12
[extra]
banner = "https://static.graphite.art/content/blog/2022-05-12-distributed-computing-in-the-graphene-runtime__2.avif"
banner_png = "https://static.graphite.art/content/blog/2022-05-12-distributed-computing-in-the-graphene-runtime__2.png"
author = "Keavon Chambers"
summary = "Graphite's 2D editor is built upon Graphene, a node-based editing system for nondestructive design across various data types designed to render artwork faster using multiple machines. The system optimizes execution paths, minimizes latency, and uses a distributed runtime for quick data processing."
reddit = "https://www.reddit.com/r/graphite/comments/unw45k/blog_post_distributed_computing_in_the_graphene/"
twitter = "https://twitter.com/GraphiteEditor/status/1524664083554791424"
+++
Graphite is a professional 2D graphics editor for photo editing, image manipulation, graphic design, illustration, data visualization, batch processing, and technical art. It is designed to run on a variety of machines, from mobile hardware like iPads or web browsers on midrange laptops up to beefy workstations with dozens of CPU cores and multiple GPUs.
To provide a responsive user experience, its architecture is made to support the use of distributed computation to make up for deficiencies in local compute power. Resulting productivity benefits will scale for users on hardware ranging from low-end mobile devices up to high-end workstations because documents and use cases can grow to great complexity.
This article explores the current thinking about the problems and potential engineering solutions involved in Graphite and Graphene for building a high-performance distributed computing runtime environment. We are only just embarking on the graph engine implementation, meaning this post describes theoretical approaches to theoretical challenges. The aim is to shed light on what will need to be built and what we currently believe is the trajectory for this work. We hope this post prompts discussions that evolve the concepts and approaches described herein. If this topic sounds interesting and you have feedback, please [get in touch](/contact).
<!-- more -->
## Node-based editing
A core feature is Graphite's reliance on procedural content generation using a node graph engine called Graphene. In traditional editors like Photoshop and Gimp, certain operations like "blur the image" modify the image pixels permanently, destroying the original (unblurred) image information.
Graphite is a *nondestructive* editor. Its approach is to represent the "blur" operation as a step in the creation process. All editing steps made by the user are encoded as operations, such as: import or resize an image, draw with a paintbrush, select an area with a certain color, combine two geometric shapes, etc.
Operations are functions that process information and are called *nodes*. For example, the "Blur" node takes an image and a strength value and outputs a blurred version of the image. On the advanced end, machine learning-powered nodes may do things like image synthesis or style transfer. Many nodes perform a wide variety of image editing operations and these are connected together into a directed acyclic graph where the final output is the pixels drawn to the screen or saved to an image file.
Many nodes process raster image data, but others work on data types like vector shapes, numbers, strings, colors, and large data tables (like imports from a spreadsheet, database, or CSV file). Some nodes perform visual operations like blur while others modify data, like performing regex matching on strings or sorting tables of information. For example, a CSV file might be imported, cleaned up, processed, then fed into the visual nodes which render it in the form of a chart.
Different nodes may take microseconds, milliseconds, seconds, or occasionally even minutes to run. Most should not take more than a few seconds, and those which take appreciable time should run infrequently. During normal operations, hundreds of milliseconds should be the worst case for ordinary nodes that run frequently. Caching is used heavily to minimize the need for recomputation.
### Node authorship
The nodes that process the node graph data can be computationally expensive. The goal is for the editor to ordinarily run and render (mostly) in real-time in order to be interactive. Because operations are arranged in a directed acyclic graph rather than a sequential list, there is opportunity to run many stages of the computation and rendering in parallel.
Nodes are implemented by us as part of a built-in library, and by some users who may choose to write code using a built-in development environment. Nodes can be written in Rust to target the CPU with the Rust compilation toolchain (made conveniently accessible for users). The same CPU Rust code can be reused (with modifications where necessary) for authoring GPU compute shaders via the [rust-gpu](https://github.com/EmbarkStudios/rust-gpu) compiler. This makes it easier to maintain both versions without using separate code files and even languages between targets.
### Sandboxing
For security and portability, user-authored nodes are compiled into WebAssembly (Wasm) modules and run in a sandbox. Built-in nodes provided with Graphite run natively to avoid the nominal performance penalty of the sandbox. When the entire editor is running in a web browser, all nodes use the browser's Wasm executor. When running in a distributed compute cluster on cloud machines, the infrastructure provider may be able to offer sandboxing to sufficiently address the security concerns of running untrusted code natively.
## The Graphene distributed runtime
In the product architecture, Graphene is a distributed runtime environment for quickly processing data in the node graph by utilizing a pool of CPU and GPU compute resources available on local and networked machines. Jobs are run where latency, speed, and bandwidth availability will be most likely to provide a responsive user experience.
<img src="https://static.graphite.art/content/blog/2022-05-12-distributed-computing-in-the-graphene-runtime/local-and-cloud.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Graphene in a local and cloud environment" />
### Scheduler
If users are running offline, their CPU threads and GPU (or multiple GPUs) are assigned jobs by the local Graphene scheduler. If running online, some jobs are performed locally while others are run in the cloud, an on-prem compute cluster, or just a spare computer on the same network. The schedulers generally prioritize keeping quicker, latency-sensitive jobs local to the client machine or LAN while allowing slower, compute-intensive jobs to usually run on the cloud.
Each cluster in a locality—such as the local machine, an on-prem render farm, and the cloud data center—runs a scheduler that commands the available compute resources. The multiple schedulers in different localities must cooperatively plan the high-level allocation of resources in their respective domains while avoiding excessive chatter over the network about the minutiae of each job invocation.
### Cache manager
Working together with the scheduler, the Graphene cache manager stores intermediate node evaluation results and intelligently evicts them when space is limited. If the user changes the node graph, it can reuse the upstream cached data but will need to recompute downstream nodes where the data changed. Memoization can be used to avoid recomputation when an input to a node is the same as received in the past, and that result is available in the cache.
### Progressive enhancement
The scheduler and cache manager work in lockstep to utilize available compute and cache storage resources on the local machine or cluster in order to minimize latency for the user. Immediate feedback is needed when, for example, drawing with a paintbrush tool.
Sometimes, nodes can be run with quality flags. Many nodes (like the paintbrush rasterizer) are implemented using several different algorithms that produce different results trading off speed for quality. This means it runs once with a quick and ugly result, then runs again later to render a higher quality version, and potentially several times subsequently to improve the visual fidelity when the scheduler has spare compute resources and time. Anti-aliasing, for example, will usually pop in to replace aliased renders after a few seconds of waiting.
### Batched execution
It is important to reduce the overhead between executions. Sometimes, Graphene will predict, or observe during runtime, the frequent execution of code paths (or rather, *node paths*) with significant sequential (not parallel) execution steps. These are good candidates for optimization by reducing the overhead between each execution.
In these cases, Graphene will batch multiple sequentially-run nodes by recompiling them such that they are inlined as one execution unit. This can happen for both CPU-based programs and GPU-based compute shaders. This is conceptually similar to how just-in-time (JIT) compilers can predict, or observe at runtime, frequently-run code paths in order to apply compiler optimizations where they are most impactful.
### Data locality
When dealing with sequential chains of nodes, if they haven't already been recompiled as a batched execution unit, the Graphene scheduler may also frequently prioritize grouping together a set of related nodes to be run together on the same machine for memory and cache locality.
Sequential nodes allocated to the same physical hardware can avoid the overhead of copying data over the internet, or between machines in the cluster, or between RAM and VRAM, or from RAM to the CPU cache.
Graphene should recognize when a certain intermediate result already lives in RAM or VRAM and prioritize using that CPU or GPU to compute the tasks which rely on that data. But when that's not possible, we need a fast architecture to transfer data between machines. For GPUs, it might be possible to use a DMA (Direct Memory Access) strategy like Microsoft's new DirectStorage API for DirectX and transfer data into or out of VRAM straight to SSDs or networked file systems. Efficiently transferring the final result, and maybe occasionally intermediate cache results, between networks (like the cloud and client) with latency and bandwidth considerations is also important.
## Conclusion
Presently, we are experimenting with CPU and GPU node composition for the beginnings of the Graphene visual programming language. Most of what was described in this post will likely evolve as we get further into the implementation stage and when we learn more from experts in the fields of computer science that Graphene overlaps with.
If you have a background or interest in programming language design, functional programming, ECS and data-oriented design, scheduling, distributed computing, general-purpose GPU compute (GPGPU), or high-performance computing (HPC), we'd love to have your ideas steer our work. Or better yet, join the team to make this dream a reality. We discuss most of our architecture and designs on our [Discord server](https://discord.graphite.art) through text and sometimes voice. Please come say hi!
@@ -0,0 +1,198 @@
+++
title = "Looking back on 2023 and what's next"
date = 2024-01-01
[extra]
banner = "https://static.graphite.art/content/blog/2024-01-01-looking-back-on-2023-and-what's-next.avif"
banner_png = "https://static.graphite.art/content/blog/2024-01-01-looking-back-on-2023-and-what's-next.png"
author = "Keavon Chambers"
summary = "Looking back on 2023, we reflect on our significant achievements and milestones. As we move forward, we're excited to share what's next, promising a year filled with innovation and progress."
reddit = "https://www.reddit.com/r/graphite/comments/18xmoti/blog_post_looking_back_on_2023_and_whats_next/"
twitter = "https://twitter.com/GraphiteEditor/status/1742576805532577937"
js = ["/js/component/youtube-embed.js"]
css = ["/component/youtube-embed.css"]
+++
The new year is here, and with so many accomplishments to share from the past twelve months, let's revisit the highlights of 2023 for the Graphite project. Now that winter has entered, let's swing back to the spring, summarize the summer, and follow this fall's noteworthy developments that brought another year of fruitful progress to Graphite's mission of re-envisioning artists' 2D creative workflows with the best free software we can build for the open source community. This past year as a team, we all got closer— to one another from continents apart; to visiting and connecting with our industry peers; and to reaching exciting new development milestones.
<!-- more -->
I am grateful to everyone who has placed their faith in my vision for Graphite since I laid forth the design and wrote its first line of code nearly three years ago. Meeting some of the amazing people this summer who helped to make it possible, and inspired the project in the first place, was a pleasure and an honor. From California to Europe and back again, my combined family vacation and Graphite outreach tour was an opportunity to make connections with those helping us reach our goals. This blog post is both a project update and a public thank-you to those who generously lent their time and attention to our small-but-growing project. And for readers eager for an update on the software itself, stick around (or skip ahead) for a development progress report and a look at what's coming down the pipeline in the new year.
<div class="video-background" style="text-align: center">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-01-01-looking-back-on-2023-and-what's-next/christmas-tree-lights.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-01-01-looking-back-on-2023-and-what's-next/christmas-tree-lights.mp4" type="video/mp4" />
</video>
</div>
<center><em>Happy Holidays from the Graphite team!<br />These procedural light strands are powered by the newly completed node graph features.<br /><a href="https://editor.graphite.art/#demo/procedural-string-lights">Click here to explore this demo</a> — drag the wire layer's points with the Path tool.</em></center>
## The Alpha 2 release series
February marked the start of our second year developing Graphite under the alpha release banner. We took the opportunity to declare the start of a new release series, Alpha 2, for the year's focus on integrating the node graph. That goal was a success, and we anticipate the next release series, Alpha 3, will begin next month in February with a focus on procedural art workflows in line with our [roadmap](/features#roadmap).
## GDC and meetups with the Rust graphics community
In March, I attended the Game Developers Conference (GDC) in nearby San Francisco to network with professionals in the creative industry. I was accompanied by [Oliver Davies](https://github.com/otdavies), a Graphite founding collaborator, fellow 3D artist and graphics engineer, and a life-long friend of mine. We connected with Francesco Siddi and Dalai Felinto from the Blender Foundation and introduced them to the project. The conference was also an opportunity to meet face-to-face with subject-matter experts with whom I'd earlier conversed online. Because we write Graphite in the Rust programming language, we attended an impromptu meetup among game developers using Rust in the nearby park that stretched for several insightful hours of pertinent conversation topics, thusly concluding the last day of the event.
Later, in May, I went to another Rust developers meetup here in the Bay Area together with [Leonard Pauli](https://twitter.com/leonardpauli), a Graphite community member and code contributor who was in town on a visit all the way from Sweden. The event was headlined with a [presentation](https://www.youtube.com/watch?v=XjbVnwBtVEk) by [Raph Levien](https://raphlinus.github.io/) about [Xilem](https://github.com/linebender/xilem), an under-development GUI toolkit that Graphite may adopt someday for its promise of powering native, speedy desktop user interfaces. Raph Levien is a researcher and expert in the fields of 2D vector graphics, GPU-accelerated rendering, and the mathematics of splines and curves— topics considerably overlapping with Graphite's own technical disciplines. The meetup was a nice face-to-face introduction before I'd end up seeing Raph and Leonard each again very soon.
<p><img src="https://static.graphite.art/content/blog/2024-01-01-looking-back-on-2023-and-what's-next/bay-area-rust-meetup.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.jpg')" alt="Raph Levien speaking about Xilem" /></p>
<center><em>Raph gives his talk about Xilem and GUIs in Rust</em></center>
The next week I accepted Raph's invitation to visit him at his employer, Google, in Mountain View where we spent several hours talking shop. His other flagship open source library, [Vello](https://github.com/linebender/vello), is the high-performance 2D vector graphics renderer we plan to use as a crucial part of Graphite's render pipeline. Our discussions dove into the history and goals of Graphite, our shared research challenges, and covered some fascinating details surrounding Vello, computational geometry, and GPU rendering. As we round out 2023, Graphite's roadmap is finally nearing the stage of integrating Vello in the coming weeks and I look forward to growing our collaboration with Raph and his [research group](https://linebender.org/).
## Embark Studios visit in Stockholm, Sweden
<p><img src="https://static.graphite.art/content/blog/2024-01-01-looking-back-on-2023-and-what's-next/embark.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.jpg')" alt="Leonard and Keavon in front of the Embark office sign" /></p>
<center><em>Leonard (left) and Keavon (right) at Embark Studios</em></center>
In June, as part of a vacation with my family to Europe, I caught up again with Leonard and he led us on a tour of his beautiful city of Stockholm. I also reached out to Johan Andersson, CTO of Embark Studios and an ambassador for the open source Rust developer community. Embark very generously contributes open source libraries for the Rust computer graphics ecosystem that are vital to Graphite, including [rust-gpu](https://github.com/EmbarkStudios/rust-gpu) which powers our node graph engine's compilation to GPU compute shaders. Johan showed Lenoard and me around Embark and we all chatted about what each of us are pursuing with Rust in the creative software industry. (By the way, Embark just released their first game, [The Finals](https://www.reachthefinals.com/)— check it out!)
## Blender visit in Amsterdam, The Netherlands
<p><img src="https://static.graphite.art/content/blog/2024-01-01-looking-back-on-2023-and-what's-next/blender-hq.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.jpg')" alt="Graphite and Blender team members in front of the Blender building" /></p>
<center><em>Left to right: Dalai, Dennis, Keavon, Ton, Francesco</em></center>
The next month in July, together with Graphite's lead engineer [Dennis Kobert](/about#dennis-kobert), we spent the afternoon visiting Blender's headquarters in Amsterdam. Blender has been, since the beginning, my inspiration and motivation for taking on the tremendously ambitious goal of building Graphite. As fellow open source software projects building digital content creation tools, this was a wonderful chance to see where the magic happens and meet the people behind the curtain.
At the invitation of Francesco Siddi, COO of the Blender Foundation whom I'd met earlier at GDC, Dennis and I presented a lunchtime talk for the staff to introduce the Graphite project. Ton Roosendaal, Blender's founder, kindly cooked up some scrumptious, lovingly-made meals of fried eggs for us and his team. We spent a couple hours mingling— answering and asking questions and chatting about design and technical topics ranging from our Rust node graph language infrastructure to Blender's experience with color science standards.
<p><img src="https://static.graphite.art/content/blog/2024-01-01-looking-back-on-2023-and-what's-next/blender-presentation.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.jpg')" alt="Keavon presents in front of a projector screen at Blender's offices" /></p>
<center><em>Keavon presents to the Blender staff</em></center>
Ton also very generously gave us his time and full attention with an office hour for Dennis and me to ask his advice and learn from the three decades of experience that led to Blender's decisive success. (On January 2, Blender turns 30! They're aiming for a goal of 10,000 supporters to [donate a birthday gift](https://fund.blender.org/). I encourage you to join me in doing so.)
## Graphite developer retreat in Karlsruhe, Germany
<p><img src="https://static.graphite.art/content/blog/2024-01-01-looking-back-on-2023-and-what's-next/karlsruhe.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.jpg')" alt="Keavon and Dennis in front of Karlsruhe Palace" /></p>
<center><em>Keavon (left) and Dennis (right) at Karlsruhe Palace</em></center>
The week before the Blender visit, I arrived in Karlsruhe, Germany for a two-week stay with Dennis for our first-ever team retreat. After spending hundreds of hours collaborating online, it was great to finally meet in-person. From diving deep into Graphite design discussions, to exploring the city and his college campus and joining his friends in social activities (and thus meeting another Graphite contributor, Isaac Körner, recruited by Dennis), it was a very welcoming and productive exchange.
## SIGGRAPH in Los Angeles, California
<p><img src="https://static.graphite.art/content/blog/2024-01-01-looking-back-on-2023-and-what's-next/siggraph.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.jpg')" alt="Keavon and Oliver standing in front of the SIGGRAPH conference sign" /></p>
<center><em>Keavon (left) and Oliver (right) at SIGGRAPH</em></center>
After returning back home to the States, the following month in August I road tripped down south to Los Angeles for another conference visit with Oliver Davies. SIGGRAPH is the industry's largest computer graphics conference which presented numerous opportunities to network with others in the field. Blender also exhibits each year and I got to meet up again with the crew from Amsterdam and meet some new faces who were absent in our earlier visit. I look forward to returning to LA this coming April for the first full Blender Conference held in the US and connecting with more like-minded open source creative aficionados.
## Incorporating Graphite Labs, LLC
The next big news of August was my formation of [Graphite Labs, LLC](https://www.linkedin.com/company/graphite-labs) as a legal entity, allowing us to open a bank account and sign contracts on behalf of the Graphite project. This is an important step in the professional growth of the project. A tax-exempt nonprotfit foundation may happen in the future, but an LLC is a more accessible starting point. This step has opened up opportunities to form partnerships with industry, collect [sponsorships and donations](/donate), and hire full-time engineers a few years down the road once we have the income to financially support others developing Graphite. A major goal in 2024 is growing the sustainability and financial independence of the organization and allowing myself a modest income stream to offset costs while continuing my full-time Graphite work.
## Website, user manual, and tutorials
I allocated my time at several points throughout the year into growing and evolving this website with a refreshed and more visually-appealing home page, dedicated pages for information [about](/about) the project and its [features](/features), an area providing resources and help for [volunteers](/volunteer) and [code contributors](/volunteer/guide), and just this month— a [user manual](/learn) complete with an introductory tutorial series. The first video went up yesterday:
{{ youtube_embed(id="7gjUhl_3X10", title="Vector Art Quickstart - Graphite, the Open Source 2D Graphics Suite") }}
The user manual and tutorial series will continue expanding throughout the coming weeks. Additional website features including user accounts, forums, and other community features are being planned.
## 2023 development progress report
<p><img src="https://static.graphite.art/content/blog/2024-01-01-looking-back-on-2023-and-what's-next/commit-rate-graph.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Graph visualizing the number of code commits each week of 2023" /></p>
<center><em>Weekly code commit rate in 2023</em></center>
Starting out the year twelve months ago, Graphite's core vector editing tools were in good shape and the node graph engine had just reached its experimental beginnings as a way of applying color filters to bitmap images. Further developing the graph engine (called Graphene) and integrating it throughout every part of the Graphite editor thus became the focus for 2023.
While much of the team's time was spent on refactors to swap short-term placeholder code with Graphene-powered replacements, this one-step-back, two-steps-forward approach has ultimately led to exciting new capabilities for users to design procedurally-generated vector art. I'm aware of no other vector graphics editor with a node-based procedural editing workflow, so this makes me especially thrilled to release the first app of its kind with that unique and useful capability.
But 2023 wasn't only put towards refactoring code. New features were added all throughout the year and here are a few favorites.
- Graphene's node infrastructure has seen steady developments which significantly upgraded the power of the node graph, its performance, and its frontend usability.
- There are lots of new nodes that do neat things! From complex color adjustments like Vibrance to procedural building blocks like Copy to Points and noise pattern generators, there are plenty to try out (and so many more coming in 2024).
- The (still rudimentary) Brush tool was added for drawing simple raster-based sketches.
- Drawing custom vector shapes with the Pen and Path tools saw usability improvements with point selection, nudging/transformation, and entering exact numerical positions.
- Number input boxes in the UI can now be dragged to update their values and have math expressions evaluated automatically for convenience. Double a value just by typing `*2` at the end, or take the square root by wrapping it within `sqrt(` and `)`.
- A button to quickly open [pre-made sample art](https://editor.graphite.art/#demo/valley-of-spires) documents was included at the suggestion of the fine folks at Blender. This helps new users see Graphite in action instead of just opening up an overwhelmingly barren blank canvas.
## Integrating the node graph
With so much to change in the goal of rewriting nearly every system with its Graphene counterpart, we had to take an incremental path so other feature development could continue without a broken editor. As succinctly as possible, this is the story of how we pulled it off— although this section gets rather technical so feel free to skip past if that's not your cup of tea.
The previous, intentionally-temporary layer system supported folders, vector shapes, text, bitmap images, and dynamic image generation.
<p><img src="https://static.graphite.art/content/blog/2024-01-01-looking-back-on-2023-and-what's-next/migration-block-diagram-1.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Block diagram of a Graphite document before the node graph" /></p>
<center><em>Block diagram before the node graph</em></center>
To begin the incremental integration of nodes, we first added another type of layer, housing an instance of a Graphene node graph, which would supersede the other types. Then began the long process of porting all the other "legacy" layer types—and the viewport tools that operated on them—to become nodes. So the text layer type became a Text node managed by the Text tool, for example. This first phase was completed by April.
<p><img src="https://static.graphite.art/content/blog/2024-01-01-looking-back-on-2023-and-what's-next/migration-block-diagram-2.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Block diagram of a Graphite document after the first phase of migration" /></p>
<center><em>Block diagram after the first phase</em></center>
Just folders and graph-driven legacy layers remained, but for the second phase, these (as well as artboards which were a separate temporary system) had to be combined into a single graph. Even with graph-based legacy layers, nodes couldn't yet interact between layer graphs to create interesting procedural designs. The Graphite vision calls for a single unified graph per document where all content lives, organized by layers, folders, and artboards collectively living within that graph. So phase two began with adding another node graph instance attached to the document itself instead of any particular layer. Then the old artboard system was replaced by artboard nodes in that graph, providing white backgrounds for the pages of artwork drawn atop by layers. Next, we built a new form of layer that would live in the node graph itself to provide organizational structure, acting as both a container for artwork nodes and a folder for other layers. By August, users could edit the document graph by hand but the viewport tools, folder hierarchy, rendering pipeline, and numerous other systems all still used the legacy layers.
<p><img src="https://static.graphite.art/content/blog/2024-01-01-looking-back-on-2023-and-what's-next/migration-block-diagram-3.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Block diagram of a Graphite document after the second phase of migration" /></p>
<center><em>Block diagram after the second phase</em></center>
For the third phase, we had to yet again port each of the viewport tools so they would operate on the unified document graph instead of the legacy layer graphs. But the incremental approach ended here— this last phase had to happen all at once, which posed a challenge for developing the editor while numerous features were fully broken pending rework. We began with a separate development branch, always kept up-to-date with the latest editor code changes, for a couple months until breakages were reduced to an acceptable level, then integrated with the main codebase in October. By tracking and burning down the list of [62 outstanding issues and regressions](https://github.com/GraphiteEditor/Graphite/issues/1394) one-by-one, in mid-December we finally reached our long-sought goal: deploying a new stable release of Graphite featuring the unified node graph! I'd like to extend an extra big thank-you to core team member ["Hypercube"](/about#hypercube) for the dedication and persistence in grinding through most of these.
<p><img src="https://static.graphite.art/content/blog/2024-01-01-looking-back-on-2023-and-what's-next/migration-block-diagram-4.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Block diagram of a Graphite document now that the migration is complete" /></p>
<center><em>Block diagram of the completed integration</em></center>
I followed this up over my Christmas holiday by hunting down and ripping out over 6000 lines of unused code, satisfyingly bringing Graphite's total lines of Rust down to under 50,000— just about the same number as when we began, despite adding a year's worth of functionality! Fewer lines to understand and maintain makes our jobs easier, and I'm pleasantly surprised at how efficiently the Graphite team has managed to represent the app's considerable functionality in so relatively few lines. This is a sign of good engineering practices and it makes me proud of our capable team and what we have accomplished together.
## Looking ahead to 2024
The hard part is done. The unified document node graph, now that it's complete, paves the pathway towards new feature development for vector and raster editing. The Alpha 2 release series is nearly ready to become Alpha 3 with a focus towards procedural editing as the theme for the year ahead. And hopefully the road to the Beta release series, and then 1.0, is not too much further ahead.
First, I have some high-level goals for 2024:
- Cultivate a larger active community and grow the core team so we can move from a linear to an exponential pace of development
- Begin sending quarterly email [newsletters](/#newsletter), publish these blog posts more frequently, and find a dedicated volunteer to assist in writing them while also growing Graphite's social media and internet presence
- Announce Graphite to a wide audience and grow the daily active users by 10x or more, especially among artists
- Reach 20,000 [stars on GitHub](https://github.com/GraphiteEditor/Graphite/stargazers) (we just passed 5000 this December)
- Move towards a greater focus on polish, stability, performance, and learning resources for the product
- Attain sustainable income from donors and sponsors, and maybe even apply for grants in order to hire a full-time developer
- Build infrastructure for user accounts and prove the viability of getting revenue from hosted cloud rendering
And then from a development perspective, I am looking forward to accomplishing these overarching objectives in 2024:
- Restoring several previous features that were removed during refactors in the past year to a fully working state including snapping, folder bounding boxes, transform pivots, and vector shape boolean operations
- Deploying GPU-based rendering by default and moving from an experimental to a production-ready hardware-accelerated compositing system using [Vello](https://github.com/linebender/vello) to unify the currently separate raster and vector pipelines
- Designing a new vector graphics data format suitable for advanced procedural editing and rendering, plus the associated procedural workflow features
- Remaking the Brush tool with the GPU-accelerated pipeline and the adaptive resolution system so digital painting in Graphite becomes practical
- Implementing the Mask Mode feature for Magic Wand tool marquee selections, which will dramatically improve Graphite's utility as a raster graphics editor
- Supporting animation capabilities (a potential stretch goal for the year)
## A call for community
Achieving everything listed above is ambitious, but it's ambition that has brought us to where we are today. Pulling this off will require a larger team and more resources than we've had in 2023. So if the mission we are striving for is exciting and you agree the world needs a truly great and versatile open source 2D graphics suite, we need your help!
- Technically inclined developers interested in Rust, web dev, computer graphics, backend programming, compilers, machine learning, mathematics, or any of the other varied disciplines that Graphite overlaps with— we likely have a role or project for you.
- We also have self-contained research projects involving problem-solving outside an existing codebase. One example: there are numerous industry-standard image filter effects we'd like to implement in Graphite where a volunteer could run analysis on the colors of test images to identify a suitable algorithm that gives matching results. We hope to assemble a larger Discord community of motivated people we can tap for help in solving these sorts of problems. These also make great university term projects and we've mentored several groups successfully in the past, so please reach out.
- Technical artists with experience in procedural editing tools and engineers who enjoy designing solutions for complex problems would also be highly valuable community members when it comes to taking part in the many large, nuanced product and architecture design decisions we'll have to make this year.
- Graphic designers and artists who put the time into using Graphite on a regular basis and helping us learn its practical strengths and weaknesses would also be valuable contributors. Helping with the creative parts of maintaining the app and assisting new users who have questions in the growing community would relieve the burden from the core team.
- And spread the word! Create tutorial videos. Show off your creations on social media. Use it in your classroom. 2024 is the year Graphite is ready to come out of the shadows and get discovered.
If your New Year's resolution is joining an open source project, consider Graphite! We work hard to help new community contributors get up to speed with resources and guidance. We frequently hear praise that Graphite is a very inviting and supportive project from volunteers who have not had great prior experiences trying to get involved in open source. Join the Graphite [Discord server](https://discord.graphite.art) and reach out to me (@Keavon) about how you'd like to get involved.
## Launching our supporter fund
There's one last big way you can help and keep Graphite from needing to turn to investors who would someday come knocking for exponential profits at the expense of you, the user. That doesn't align with my vision so I have been self-funding Graphite for the past three years. But I, alone, can't keep that going for a fourth year and beyond.
With a laser-tight focus on completing our 2023 development objectives, I haven't yet called out for donations until now. You can be the very first person to join at the level of a Supporter (starting at $10 monthly) or Sponsor (starting at $50 monthly for individuals and $100 monthly for companies). Please consider joining at one of the levels [listed here](/donate).
Please help launch Graphite towards the 2024 goal of attaining self-sufficiency so I can maintain my full-time commitment to an independent Graphite for the long haul. Thank you, it really means the world to me. ♥
<a href="/donate" class="button arrow">Become a supporter</a>
## Wrapping up
Thank you to our community for an incredible year. Passing [5000 stars](https://github.com/GraphiteEditor/Graphite/stargazers) on the project GitHub repository was a wonderful gift this holiday season. Everyone who has given advice, written code, and expressed enthusiasm has inspired me every day to stay focused and motivated. It was especially a pleasure and honor to visit the many people and organizations mentioned in this post throughout the past year. The reception by all has been heartwarming and I look forward to staying connected with them and a growing Graphite community in the promising year that now lies ahead. I am most of all excited for the awesome state Graphite will be in when it comes time to write this post again next year.
Now go [make some awesome art](https://editor.graphite.art)!
@@ -0,0 +1,25 @@
+++
title = "Graphite internships: announcing participation in GSoC 2024"
date = 2024-02-22
[extra]
banner = "https://static.graphite.art/content/blog/2024-02-22-graphite-internships-announcing-participation-in-gsoc-2024.avif"
banner_png = "https://static.graphite.art/content/blog/2024-02-22-graphite-internships-announcing-participation-in-gsoc-2024.png"
author = "Keavon Chambers"
summary = "Join Graphite in Google Summer of Code 2024 for a unique opportunity to contribute to open-source software development in Rust and computer graphics. Get paid while learning, working on self-contained projects under experienced mentors, and help Graphite grow."
reddit = "https://www.reddit.com/r/graphite/comments/1ax3l8z/blog_post_graphite_internships_announcing/"
twitter = "https://twitter.com/GraphiteEditor/status/1760619083396165703"
+++
Calling Rust and computer graphics developers: spend your summer contributing to open source and get paid by the Google Summer of Code program. We are excited to announce our first year participating as a mentoring organization in GSoC 2024. Interested prospective applicants should get involved right away and prepare proposals by the April 2 deadline.
<!-- more -->
[Graphite](/) is a new 2D graphics editor bringing a modern, nondestructive approach to creative tooling by merging both traditional raster and vector workflows with node-based compositing and procedural generation.
[Google Summer of Code](https://summerofcode.withgoogle.com/), now entering its 20th year, is a worldwide program that supports students getting involved in open source software development. Like other forms of summer internships, GSoC pays a [stipend](https://developers.google.com/open-source/gsoc/help/student-stipends) for participants to work on self-contained projects with the direction and guidance of experienced mentors. The program is directed towards university students but is open to anyone 18 and older. The [timeline's](https://developers.google.com/open-source/gsoc/timeline) start date is May 27 and most projects run for 12 weeks, but there is some flexibility towards pacing and end dates.
We are looking for reasonably experienced, self-motivated, fast-learning students who are interested in Rust and/or computer graphics (and for one project, machine learning). Full [project descriptions](/volunteer/guide/student-projects/#project-idea-list) are listed and we encourage prospective applicants to reach out right away to maximize the chance of acceptance.
Additional year-round opportunities are available for student capstone or independent research projects. These are run similarly, but are also available to multi-person groups and provide academic credit instead of a stipend. To date, three were [completed](/volunteer/guide/student-projects/#successful-past-projects) successfully.
It is our hope that GSoC will help Graphite grow and provide valuable learning outcomes for participants. A win-win! If this is an opportunity that's right for you, begin [getting involved](/volunteer/guide/student-projects/).
@@ -0,0 +1,248 @@
+++
title = "Graphite progress report (Q1 2024)"
date = 2024-05-09
[extra]
banner = "https://static.graphite.art/content/blog/2024-05-09-graphite-progress-report-q1-2024__2.avif"
banner_png = "https://static.graphite.art/content/blog/2024-05-09-graphite-progress-report-q1-2024__2.png"
author = "Keavon Chambers & Hypercube"
summary = "Graphite's Q1 2024 update introduces a precise snapping system and a customizable grid for enhanced design control. The update also includes improved procedural scattering with the 'Copy to Points' node, demonstrated in new demo artwork."
reddit = "https://www.reddit.com/r/graphite/comments/1coa0if/blog_post_graphite_progress_report_q1_2024/"
twitter = "https://twitter.com/GraphiteEditor/status/1788698448348266946"
css = ["/component/demo-artwork.css"]
+++
[Graphite](/) is a new 2D vector graphics editor bringing a modern, nondestructive approach to creative workflows with node-based procedural generation. The project is currently three years into development, with a focus on streamlining the creative process for procedural vector artwork. See the [roadmap](/features#roadmap) for a more in-depth summary of the goals for 2024 and beyond.
<!-- more -->
Starting in 2024, we are now publishing quarterly reports to summarize the new features and improvements made to Graphite. If you missed the [2023 year in review](../looking-back-on-2023-and-what-s-next), be sure to check it out after this. We anticipate sending our first email newsletter (with more to follow roughly quarterly) in the near future as well, so be sure to [subscribe](/#newsletter) if you haven't already.
Over the first three months of the year, we are delighted to have seen many contributions both from new and recurrent contributors, including substantial interest by students through [Google Summer of Code](/blog/graphite-internships-announcing-participation-in-gsoc-2024/). We would like to send a big thanks to all of the contributors who made this progress happen. If you are interested in getting involved or just following development, see the [contributor guide](/volunteer/guide) and join [our Discord](https://discord.graphite.art).
All Q1 2024 commits may be [viewed in this list](https://github.com/GraphiteEditor/Graphite/commits/master/?since=2024-01-01&until=2024-03-31) and all noteworthy changes are detailed below. As two of the major new features are the grid and snapping systems, the *Isometric Fountain* artwork shown on this blog post demonstrates what those features can achieve.
<div class="demo-artwork">
<a href="https://editor.graphite.art/#demo/isometric-fountain">
<img src="https://static.graphite.art/content/blog/2024-05-09-graphite-progress-report-q1-2024__2.png" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Vector art of Isometric Fountain" />
</a>
<p>
<span>
<em>Isometric Fountain</em>
</span>
<br />
<span>
<a href="https://editor.graphite.art/#demo/isometric-fountain">Open this artwork</a> to<br />explore it yourself.
</span>
</p>
</div>
<!--
EDITOR'S NOTE: The grammatical structure of each bullet point should follow the form: "Check out our new... [Bullet point description]"
-->
## Additions
- *Copy to Points* node improvements, including randomization of scale and rotation with biasing <small>([#1540](https://github.com/GraphiteEditor/Graphite/pull/1540), [#1541](https://github.com/GraphiteEditor/Graphite/pull/1541), [commit ed82c5](https://github.com/GraphiteEditor/Graphite/commit/ed82c5f20fccd66a959334dee33351657968cdb6), [#1592](https://github.com/GraphiteEditor/Graphite/pull/1592), [commit 7e5069](https://github.com/GraphiteEditor/Graphite/commit/7e5069f638cfcc3e7af21f32eded67a005490402))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-05-09-graphite-progress-report-q1-2024/copy-to-points-node-improvements.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-05-09-graphite-progress-report-q1-2024/copy-to-points-node-improvements.mp4" type="video/mp4" />
</video>
</div>
- Improvements to the node graph UI <small>([#1564](https://github.com/GraphiteEditor/Graphite/pull/1564), [#1568](https://github.com/GraphiteEditor/Graphite/pull/1568))</small>
- Grid overlay for the canvas with customizable rectangular and isometric grid lines <small>([#1521](https://github.com/GraphiteEditor/Graphite/pull/1521), [commit 5c9d3c](https://github.com/GraphiteEditor/Graphite/commit/5c9d3c5d755e67411c110c0d5fd38d991cb6696c))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-05-09-graphite-progress-report-q1-2024/grid-popover.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-05-09-graphite-progress-report-q1-2024/grid-popover.mp4" type="video/mp4" />
</video>
</div>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-05-09-graphite-progress-report-q1-2024/grid-demo-art.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-05-09-graphite-progress-report-q1-2024/grid-demo-art.mp4" type="video/mp4" />
</video>
</div>
- New and improved snapping system for aligning shapes with one another and the grid <small>([#1521](https://github.com/GraphiteEditor/Graphite/pull/1521), [#1567](https://github.com/GraphiteEditor/Graphite/pull/1567), [#1547](https://github.com/GraphiteEditor/Graphite/pull/1547), [#1570](https://github.com/GraphiteEditor/Graphite/pull/1570), [#1574](https://github.com/GraphiteEditor/Graphite/pull/1574))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-05-09-graphite-progress-report-q1-2024/snapping.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-05-09-graphite-progress-report-q1-2024/snapping.mp4" type="video/mp4" />
</video>
</div>
- *Morph* node for tweening vector shapes <small>([#1576](https://github.com/GraphiteEditor/Graphite/pull/1576))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-05-09-graphite-progress-report-q1-2024/morph-node.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-05-09-graphite-progress-report-q1-2024/morph-node.mp4" type="video/mp4" />
</video>
</div>
- Visualize which nodes are the source of a graph type error <small>([#1577](https://github.com/GraphiteEditor/Graphite/pull/1577))</small>
<img src="https://static.graphite.art/content/blog/2024-05-09-graphite-progress-report-q1-2024/graph-error-visualization.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Graph error visualization screenshot" />
- Improvements to document tilting and resetting tilt <small>([commit 8eef96](https://github.com/GraphiteEditor/Graphite/commit/8eef96511e575d58a36289f3d0e30eb68098b4e7))</small>
- SVG import <small>([#1579](https://github.com/GraphiteEditor/Graphite/pull/1579), [#1656](https://github.com/GraphiteEditor/Graphite/pull/1656))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-05-09-graphite-progress-report-q1-2024/svg-import.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-05-09-graphite-progress-report-q1-2024/svg-import.mp4" type="video/mp4" />
</video>
</div>
- Re-add Select tool functionality that was lost from a code change last year <small>([#1583](https://github.com/GraphiteEditor/Graphite/pull/1583))</small>
- *Poisson-Disk Points* node for sampling randomly distributed points in a shape, with Red Dress demo artwork <small>([#1586](https://github.com/GraphiteEditor/Graphite/pull/1586), [commit 12e16b](https://github.com/GraphiteEditor/Graphite/commit/12e16b9a4efe40fcf779c34f83d3df8b3e3542f3), [#1590](https://github.com/GraphiteEditor/Graphite/pull/1590), [#1596](https://github.com/GraphiteEditor/Graphite/pull/1596))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-05-09-graphite-progress-report-q1-2024/poisson-disk-points-node-demo-art.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-05-09-graphite-progress-report-q1-2024/poisson-disk-points-node-demo-art.mp4" type="video/mp4" />
</video>
</div>
- Pen tool point-by-point undo while drawing without wiping out the full in-progress shape <small>([#1587](https://github.com/GraphiteEditor/Graphite/pull/1587), [#1597](https://github.com/GraphiteEditor/Graphite/pull/1597))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-05-09-graphite-progress-report-q1-2024/pen-tool-point-by-point-undo.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-05-09-graphite-progress-report-q1-2024/pen-tool-point-by-point-undo.mp4" type="video/mp4" />
</video>
</div>
- Freehand tool support for extending the endpoints of open paths <small>([#1594](https://github.com/GraphiteEditor/Graphite/pull/1594), [#1623](https://github.com/GraphiteEditor/Graphite/pull/1623))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-05-09-graphite-progress-report-q1-2024/freehand-tool-endpoint-extension.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-05-09-graphite-progress-report-q1-2024/freehand-tool-endpoint-extension.mp4" type="video/mp4" />
</video>
</div>
- Path tool support for breaking a closed shape into an open path by deleting (not dissolving) a point <small>([#1593](https://github.com/GraphiteEditor/Graphite/pull/1593))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-05-09-graphite-progress-report-q1-2024/point-deletion-breaking-closed-shapes.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-05-09-graphite-progress-report-q1-2024/point-deletion-breaking-closed-shapes.mp4" type="video/mp4" />
</video>
</div>
- Path tool insertion of a point on a path segment by sliding to the desired spot <small>([#1581](https://github.com/GraphiteEditor/Graphite/pull/1581))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-05-09-graphite-progress-report-q1-2024/sliding-point-insertion.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-05-09-graphite-progress-report-q1-2024/sliding-point-insertion.mp4" type="video/mp4" />
</video>
</div>
- Box-based drag selection in the node graph <small>([#1616](https://github.com/GraphiteEditor/Graphite/pull/1616))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-05-09-graphite-progress-report-q1-2024/box-selection-in-node-graph.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-05-09-graphite-progress-report-q1-2024/box-selection-in-node-graph.mp4" type="video/mp4" />
</video>
</div>
- Auto-save the document every 30 seconds <small>([#1580](https://github.com/GraphiteEditor/Graphite/pull/1580))</small>
- Auto-panning when drawing with each interactive tool when the user's pointer extends past the edge of the viewport <small>([#1625](https://github.com/GraphiteEditor/Graphite/pull/1625), [#1652](https://github.com/GraphiteEditor/Graphite/pull/1652), [#1682](https://github.com/GraphiteEditor/Graphite/pull/1682), [#1690](https://github.com/GraphiteEditor/Graphite/pull/1690))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-05-09-graphite-progress-report-q1-2024/auto-panning-in-viewport.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-05-09-graphite-progress-report-q1-2024/auto-panning-in-viewport.mp4" type="video/mp4" />
</video>
</div>
- Launch of the Alpha 3 release series in February <small>([commit f02dd5](https://github.com/GraphiteEditor/Graphite/commit/f02dd5c0f625b25bf3510ba0e9839ca182d930e4))</small>
<img src="https://static.graphite.art/content/blog/2024-05-09-graphite-progress-report-q1-2024/alpha-3-about-graphite.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Alpha 3 release series in the 'About Graphite' menu screenshot" />
- Search functionality by typing in menu lists <small>([#1499](https://github.com/GraphiteEditor/Graphite/pull/1499))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-05-09-graphite-progress-report-q1-2024/search-in-menu-lists.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-05-09-graphite-progress-report-q1-2024/search-in-menu-lists.mp4" type="video/mp4" />
</video>
</div>
- Improvements to the dynamic input hints of several tools <small>([commit 9f8466](https://github.com/GraphiteEditor/Graphite/commit/9f84661facd545bfdfeaa5d37038abeacc00ee08), [#1667](https://github.com/GraphiteEditor/Graphite/pull/1667), [#1670](https://github.com/GraphiteEditor/Graphite/pull/1670))</small>
- *Solidify Stroke* node for converting a stroke into a filled shape <small>([#1650](https://github.com/GraphiteEditor/Graphite/pull/1650))</small>
- Path tool point selection checkbox for locking an anchor point's handles as colinear <small>([commit 5bca93](https://github.com/GraphiteEditor/Graphite/commit/5bca931813e456e2f6035844c21e77ee590b7728))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-05-09-graphite-progress-report-q1-2024/colinear-handles-checkbox.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-05-09-graphite-progress-report-q1-2024/colinear-handles-checkbox.mp4" type="video/mp4" />
</video>
</div>
## Fixes
- *Sample Points* node performance issues and bug with open paths <small>([#1542](https://github.com/GraphiteEditor/Graphite/pull/1542), [commit b3e4ca](https://github.com/GraphiteEditor/Graphite/commit/b3e4caec1aa4d7b702ac36638b968cf165780149), [commit 93aa10](https://github.com/GraphiteEditor/Graphite/commit/93aa10a76f96946314d5c5787418522423a76544), [#1546](https://github.com/GraphiteEditor/Graphite/pull/1546))</small>
- Some numerical node data incompatible with others <small>([#1543](https://github.com/GraphiteEditor/Graphite/pull/1543), [commit c14c6f](https://github.com/GraphiteEditor/Graphite/commit/c14c6fbe9345be91d04825fe8364e6639430b5e5), [#1618](https://github.com/GraphiteEditor/Graphite/pull/1618))</small>
- Incorrect placement when pasting an image <small>([commit cd61da](https://github.com/GraphiteEditor/Graphite/commit/cd61daf869780e1302de1a34bca7b79485ca69c3))</small>
- Unable to select upside-down (negative scale) layers <small>([#1560](https://github.com/GraphiteEditor/Graphite/pull/1560))</small>
- Memory leak in a type inference hashmap <small>([#1566](https://github.com/GraphiteEditor/Graphite/pull/1566))</small>
- Non-filled shapes are drawn missing the solid color field from their *Fill* node <small>([#1572](https://github.com/GraphiteEditor/Graphite/pull/1572))</small>
- *Circular Repeat* node crash when it has no input connection <small>([#1571](https://github.com/GraphiteEditor/Graphite/pull/1571))</small>
- Safari rendering bug that occurs with Graphite's floating menus <small>([commit b4dccb](https://github.com/GraphiteEditor/Graphite/commit/b4dccb865540e25a6d859e5284a50a36e361d5ee))</small>
- Performance issue affecting Windows users where every render occurred thrice <small>([commit a7bf6e](https://github.com/GraphiteEditor/Graphite/commit/a7bf6e24599fc3d7dc51699f916ba049758a2081))</small>
- Performance issue with nonfunctional caching of images <small>([#1595](https://github.com/GraphiteEditor/Graphite/pull/1595))</small>
- Numerous history steps created while dragging an input widget every frame <small>([#1584](https://github.com/GraphiteEditor/Graphite/pull/1584), [#1598](https://github.com/GraphiteEditor/Graphite/pull/1598))</small>
- Ugly anti-aliasing in overlays from non-alignment with the pixel grid <small>([#1603](https://github.com/GraphiteEditor/Graphite/pull/1603))</small>
- Dragging nodes in the graph along only one axis causes them to snap back <small>([#1619](https://github.com/GraphiteEditor/Graphite/pull/1619))</small>
- Path tool point coordinates using the wrong coordinate system <small>([#1626](https://github.com/GraphiteEditor/Graphite/pull/1626))</small>
- Layer panel grouping causing unintended reordering and loss of names <small>([#1627](https://github.com/GraphiteEditor/Graphite/pull/1627), [#1645](https://github.com/GraphiteEditor/Graphite/pull/1645), [#1672](https://github.com/GraphiteEditor/Graphite/pull/1672), [#1678](https://github.com/GraphiteEditor/Graphite/pull/1678), [#1637](https://github.com/GraphiteEditor/Graphite/pull/1637))</small>
- Keyboard shortcut modifier keys displayed in the UI with the wrong order <small>([commit 4405e0](https://github.com/GraphiteEditor/Graphite/commit/4405e01f5595c76ff8c1cdb7e6ebf752ab53943c))</small>
- Polygon tool stars and convex polygons drawn with an undesired tilt <small>([#1640](https://github.com/GraphiteEditor/Graphite/pull/1640))</small>
- Dropdown menus not navigable with with keyboard arrow keys <small>([#1630](https://github.com/GraphiteEditor/Graphite/pull/1630), [#1649](https://github.com/GraphiteEditor/Graphite/pull/1649))</small>
- Changes to a tool's options only applying after a subsequent usage <small>([#1646](https://github.com/GraphiteEditor/Graphite/pull/1646))</small>
- Orphaned child layers left behind when a group is ungrouped or deleted <small>([#1655](https://github.com/GraphiteEditor/Graphite/pull/1655))</small>
- Some tools lacking a cancelable interaction by right clicking or hitting the Escape key <small>([#1658](https://github.com/GraphiteEditor/Graphite/pull/1658), [#1664](https://github.com/GraphiteEditor/Graphite/pull/1664), [#1666](https://github.com/GraphiteEditor/Graphite/pull/1666))</small>
- Several related issues related to extraneous undo/redo history steps <small>([#1660](https://github.com/GraphiteEditor/Graphite/pull/1660), [#1668](https://github.com/GraphiteEditor/Graphite/pull/1668), [#1675](https://github.com/GraphiteEditor/Graphite/pull/1675))</small>
- Node graph not updated when switching tabs to a different document <small>([#1691](https://github.com/GraphiteEditor/Graphite/pull/1691))</small>
- Artboard deletion causes its child artwork to also get deleted <small>([#1651](https://github.com/GraphiteEditor/Graphite/pull/1651))</small>
- Path tool's point dragging gets offset when the viewport is panned <small>([#1693](https://github.com/GraphiteEditor/Graphite/pull/1693))</small>
- Incorrect scale-nudging behavior when multiple layers are selected <small>([#1699](https://github.com/GraphiteEditor/Graphite/pull/1699))</small>
- Copy-pasted layers not preserving their hidden/visible state <small>([#1698](https://github.com/GraphiteEditor/Graphite/pull/1698))</small>
- Non-editability of hidden layers that are selected <small>([#1697](https://github.com/GraphiteEditor/Graphite/pull/1697))</small>
## Internal
- Several large refactors and code cleanups <small>([#1565](https://github.com/GraphiteEditor/Graphite/pull/1565), [#1582](https://github.com/GraphiteEditor/Graphite/pull/1582), [#1620](https://github.com/GraphiteEditor/Graphite/pull/1620), [#1695](https://github.com/GraphiteEditor/Graphite/pull/1695), [#1708](https://github.com/GraphiteEditor/Graphite/pull/1708))</small>
- Refactor for the vector format to begin being based around a concept of attributes <small>([#1624](https://github.com/GraphiteEditor/Graphite/pull/1624))</small>
- Preconfigure dev containers for easy containerized development environment setup <small>([commit 99c199](https://github.com/GraphiteEditor/Graphite/commit/99c199a8f64a3557e21f5dc002fbcfb789c40632), [#1636](https://github.com/GraphiteEditor/Graphite/pull/1636))</small>
@@ -0,0 +1,238 @@
+++
title = "Graphite progress report (Q2 2024)"
date = 2024-07-31
[extra]
banner = "https://static.graphite.art/content/blog/2024-07-31-graphite-progress-report-q2-2024.avif"
banner_png = "https://static.graphite.art/content/blog/2024-07-31-graphite-progress-report-q2-2024.png"
author = "Keavon Chambers & Hypercube"
summary = "Graphite's Q2 2024 update introduces boolean path operations, a new gradient picker, layer locking, and more improvements."
reddit = "https://www.reddit.com/r/graphite/comments/1ei9ps2/blog_post_graphite_progress_report_q2_2024/"
twitter = "https://x.com/GraphiteEditor/status/1819360794028462569"
css = ["/component/demo-artwork.css"]
+++
[Graphite](/), a new open source 2D procedural graphics editor, has spent April–June introducing **boolean path operations, a new gradient picker, layer locking**, and more improvements.
Overall, editor functionality has been shaping up and becoming an all around useful tool suite, with notable reductions in rough edges for the vector graphics workflow (our initial focus). Raster and raw photo processing workflows are also now in-development by our [Google Summer of Code](/blog/graphite-internships-announcing-participation-in-gsoc-2024/) student interns. Node graph quality-of-life improvements centered around tidy node organization are also the focus of the summer work that's underway. These projects are detailed below.
<!-- more -->
This is the second of our quarterly progress report blog posts. If you missed the [first one](../graphite-progress-report-q1-2024), be sure to check it out as well. If you'd like to help speed up future progress, please consider [getting involved](/volunteer) with code, QA/bug testing, or art/marketing projects. [Donations](/donate) are also valued, as are [stars on GitHub](https://github.com/GraphiteEditor/Graphite). Follow along and partake in our [Discord community](https://discord.graphite.art), too.
All Q2 2024 commits may be [viewed in this list](https://github.com/GraphiteEditor/Graphite/commits/master/?since=2024-04-01&until=2024-06-30) and all noteworthy changes are detailed below. To showcase the much anticipated introduction of boolean path operations, the new *Painted Dreams* artwork shown here extensively utilizes nondestructive booleans.
<div class="demo-artwork">
<a href="https://editor.graphite.art/#demo/painted-dreams">
<img src="https://static.graphite.art/content/blog/2024-07-31-graphite-progress-report-q2-2024.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Vector art of Painted Dreams" />
</a>
<p>
<span>
<em>Painted Dreams</em>
</span>
<br />
<span>
<a href="https://editor.graphite.art/#demo/painted-dreams">Open this artwork</a> to<br />explore it yourself.
</span>
</p>
</div>
## Additions
<!--
EDITOR'S NOTE: The grammatical structure of each bullet point should follow the form: "Check out our new... [Bullet point description]"
-->
- Feature for locking layers from being selected in the viewport <small>([#1702](https://github.com/GraphiteEditor/Graphite/pull/1702))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-07-31-graphite-progress-report-q2-2024/layer-locking.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-07-31-graphite-progress-report-q2-2024/layer-locking.mp4" type="video/mp4" />
</video>
</div>
- Fine-grained choices in the Snapping options popover <small>([#1730](https://github.com/GraphiteEditor/Graphite/pull/1730))</small>
<img src="https://static.graphite.art/content/blog/2024-07-31-graphite-progress-report-q2-2024/snapping-options.png" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Snapping choices popover menu" />
- Corner rounding added to the *Rectangle* node <small>([#1648](https://github.com/GraphiteEditor/Graphite/pull/1648))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-07-31-graphite-progress-report-q2-2024/rectangle-corner-rounding.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-07-31-graphite-progress-report-q2-2024/rectangle-corner-rounding.mp4" type="video/mp4" />
</video>
</div>
- Rotation added to the *Repeat* node <small>([commit 72ba4dd](https://github.com/GraphiteEditor/Graphite/commit/72ba4ddfe421c0e17930ad1c2be85be2c69e04ea))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-07-31-graphite-progress-report-q2-2024/repeat-node-rotation.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-07-31-graphite-progress-report-q2-2024/repeat-node-rotation.mp4" type="video/mp4" />
</video>
</div>
- Visibility and deletion buttons added to node sections in the Properties panel <small>([commit 07fd2c2](https://github.com/GraphiteEditor/Graphite/commit/07fd2c27827e4a91ad238d790d41396a33ef2389))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-07-31-graphite-progress-report-q2-2024/hide-delete-properties-sections.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-07-31-graphite-progress-report-q2-2024/hide-delete-properties-sections.mp4" type="video/mp4" />
</video>
</div>
- Grid color customization and dotted appearance choices <small>([#1743](https://github.com/GraphiteEditor/Graphite/pull/1743))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-07-31-graphite-progress-report-q2-2024/grid-coloration.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-07-31-graphite-progress-report-q2-2024/grid-coloration.mp4" type="video/mp4" />
</video>
</div>
- Demo artwork, *Isometric Fountain*, featured in the [last blog post](../graphite-progress-report-q1-2024) <small>([commit 6b0822d](https://github.com/GraphiteEditor/Graphite/commit/6b0822d31890b6699c4533c3e828da0e9e8c9490))</small>
<div class="demo-artwork" style="justify-content: left">
<a href="https://editor.graphite.art/#demo/isometric-fountain">
<img src="https://static.graphite.art/content/blog/2024-05-09-graphite-progress-report-q1-2024__2.png" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Vector art of Isometric Fountain" />
</a>
<p>
<span>
<em>Isometric Fountain</em>
</span>
<br />
<span>
<a href="https://editor.graphite.art/#demo/isometric-fountain">Open this artwork</a> to<br />explore it yourself.
</span>
</p>
</div>
- *Area* and *Centroid* nodes which calculate a shape's interior size and center of mass, respectively <small>([#1729](https://github.com/GraphiteEditor/Graphite/pull/1729), [#1749](https://github.com/GraphiteEditor/Graphite/pull/1749))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-07-31-graphite-progress-report-q2-2024/area-centroid-nodes.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-07-31-graphite-progress-report-q2-2024/area-centroid-nodes.mp4" type="video/mp4" />
</video>
</div>
- *Boolean Operation* node for combining two shape paths with a *Union*, *Subtract Front*, *Subtract Back*, *Intersect*, or *Difference* mode of cutting and combining <small>([#1759](https://github.com/GraphiteEditor/Graphite/pull/1759))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-07-31-graphite-progress-report-q2-2024/boolean-ops-1.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-07-31-graphite-progress-report-q2-2024/boolean-ops-1.mp4" type="video/mp4" />
</video>
</div>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-07-31-graphite-progress-report-q2-2024/boolean-ops-2.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-07-31-graphite-progress-report-q2-2024/boolean-ops-2.mp4" type="video/mp4" />
</video>
</div>
- Gradient picker <small>([#1778](https://github.com/GraphiteEditor/Graphite/pull/1778))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-07-31-graphite-progress-report-q2-2024/gradient-picker.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-07-31-graphite-progress-report-q2-2024/gradient-picker.mp4" type="video/mp4" />
</video>
</div>
- Labels for artboard names displayed in the viewport <small>([#1795](https://github.com/GraphiteEditor/Graphite/pull/1795))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-07-31-graphite-progress-report-q2-2024/artboard-names.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-07-31-graphite-progress-report-q2-2024/artboard-names.mp4" type="video/mp4" />
</video>
</div>
- [Dennis Kobert](https://github.com/truedoctor)'s Google Summer of Code project has begun adding raster editing infrastructure centered around GPU acceleration — to date this has included:
- *Rasterize* node for converting graphical content (like vector art) into an image within a chosen area and resolution scale <small>([#1755](https://github.com/GraphiteEditor/Graphite/pull/1755))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-07-31-graphite-progress-report-q2-2024/rasterize-node.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-07-31-graphite-progress-report-q2-2024/rasterize-node.mp4" type="video/mp4" />
</video>
</div>
- [Weekly updates](https://github.com/GraphiteEditor/Graphite/discussions/1773) are being posted while the project is ongoing
- [Elbert Ronnie](https://github.com/elbertronnie)'s Google Summer of Code project has begun building a library for decoding raw image formats, with the goal of supporting photo processing in Graphite once the project is completed — to date this has included:
- Initial code foundations including a basic custom TIFF decoder <small>([#1757](https://github.com/GraphiteEditor/Graphite/pull/1757))</small>
- Initial decoder for Sony ARW data encoding formats <small>([#1775](https://github.com/GraphiteEditor/Graphite/pull/1775), [#1776](https://github.com/GraphiteEditor/Graphite/pull/1776))</small>
- [Weekly updates](https://github.com/GraphiteEditor/Graphite/discussions/1771) are being posted while the project is ongoing
- [Adam Gerhant](https://github.com/adamgerhant)'s Google Summer of Code project has begun upgrading the node graph UI capabilities, quality-of-life features, and automatic layout management — to date this has included:
- Generalization of layers to support the concept of vertically stackable adjustment layers <small>([#1712](https://github.com/GraphiteEditor/Graphite/pull/1712), [#1738](https://github.com/GraphiteEditor/Graphite/pull/1738), [commit 244c8ad](https://github.com/GraphiteEditor/Graphite/commit/244c8ad10ad79c9ca4fcdb21260c5d5dc60b3a21), [#1763](https://github.com/GraphiteEditor/Graphite/pull/1763), [#1739](https://github.com/GraphiteEditor/Graphite/pull/1739), [#1748](https://github.com/GraphiteEditor/Graphite/pull/1748))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-07-31-graphite-progress-report-q2-2024/stackable-layers.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-07-31-graphite-progress-report-q2-2024/stackable-layers.mp4" type="video/mp4" />
</video>
</div>
- Support for viewing and editing subgraphs by double-clicking nodes with internal node networks <small>([#1750](https://github.com/GraphiteEditor/Graphite/pull/1750))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-07-31-graphite-progress-report-q2-2024/subgraph-editing.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-07-31-graphite-progress-report-q2-2024/subgraph-editing.mp4" type="video/mp4" />
</video>
</div>
- Extended support for navigating the graph just like the viewport (with autopanning on dragging near edges, working scrollbars, and zooming shortcuts) by migrating interaction code to the backend <small>([#1768](https://github.com/GraphiteEditor/Graphite/pull/1768), [#1790](https://github.com/GraphiteEditor/Graphite/pull/1790))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-07-31-graphite-progress-report-q2-2024/graph-navigation.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-07-31-graphite-progress-report-q2-2024/graph-navigation.mp4" type="video/mp4" />
</video>
</div>
- [Weekly updates](https://github.com/GraphiteEditor/Graphite/discussions/1769) are being posted while the project is ongoing
## Fixes
- Polished up design for the Layers panel <small>([commit 938a688](https://github.com/GraphiteEditor/Graphite/commit/938a688fa08511ed4a5f3c3d5bcfa26f4bd1eb47))</small>
- Inclusion of alpha in the *Extract Channel* node and removal of the now-redundant *Extract Alpha* node <small>([#1731](https://github.com/GraphiteEditor/Graphite/pull/1731))</small>
- Fixed interactive outlining of layers within the Select tool's box selection <small>([#1727](https://github.com/GraphiteEditor/Graphite/pull/1727))</small>
- Insertion of duplicated layers directly above their selected source layers <small>([#1726](https://github.com/GraphiteEditor/Graphite/pull/1726))</small>
- Crash fixes <small>([#1701](https://github.com/GraphiteEditor/Graphite/pull/1701), [#1754](https://github.com/GraphiteEditor/Graphite/pull/1754), [#1770](https://github.com/GraphiteEditor/Graphite/pull/1770))</small>
- Small bug fixes and polish for viewport tilt and zoom navigation <small>([commit 597c96a](https://github.com/GraphiteEditor/Graphite/commit/597c96a7db06fe44dbd4dc170511c011c7239073))</small>
- Regression fix for the Select tool's shallow selection mode <small>([#1725](https://github.com/GraphiteEditor/Graphite/pull/1725))</small>
- Previewable dropdown menu states and color button widget undo/redo history improvements <small>([#1598](https://github.com/GraphiteEditor/Graphite/pull/1598))</small>
- Persisting of overlays/snapping/grid state in saved documents and toggling them with hotkeys <small>([commit 7845302](https://github.com/GraphiteEditor/Graphite/commit/7845302c50705332711b6b18b4bdfe28a2f3c306))</small>
- Improved robustness to the backwards compatability of the document format (still offered on a best-effort basis) <small>([commit de84e39](https://github.com/GraphiteEditor/Graphite/commit/de84e39c4ec6c191f73702a51be0a5ec9c662642))</small>
- Visual bug fixes for shape drawing and nondeterministic one-pixel viewport shifting when recentering with <kbd>Ctrl</kbd><kbd>0</kbd> <small>([#1751](https://github.com/GraphiteEditor/Graphite/pull/1751))</small>
- Fixed bug where Path tool point insertion often wasn't working <small>([commit cf01f52](https://github.com/GraphiteEditor/Graphite/commit/cf01f522a8b57bfd625f15a10ae6b2812ed4c0a4))</small>
- Fixed bugs where the *Poisson-Disk Points*, *Repeat*, *Circular Repeat*, and *Bounding Box* node input shapes weren't using their transform <small>([#1784](https://github.com/GraphiteEditor/Graphite/pull/1784), [#1792](https://github.com/GraphiteEditor/Graphite/pull/1792))</small>
- Improvements to the Path tool's point selection behavior so clicking a selected anchor deselects all other selected points <small>([#1782](https://github.com/GraphiteEditor/Graphite/pull/1782))</small>
- Regression fix for being unable to arrange layers with no artboard present <small>([#1786](https://github.com/GraphiteEditor/Graphite/pull/1786))</small>
## Internal
- Removal of the editor "instances" concept, allowing for cleaned up JS interop code <small>([commit 19eb6ce](https://github.com/GraphiteEditor/Graphite/commit/19eb6ce0ab10065ec6acd6e49edd2f072729fc77), [commit 1ce3d59](https://github.com/GraphiteEditor/Graphite/commit/1ce3d59e0f39e6733c7fa170af00ef59fd10ffd9))</small>
- Loosened type system in Graphene to allow contravariant function arguments <small>([#1740](https://github.com/GraphiteEditor/Graphite/pull/1740), [#1746](https://github.com/GraphiteEditor/Graphite/pull/1746))</small>
## Website and documentation
- [Roadmap](/features#roadmap) updates featuring new features and icons <small>([commit 6a1a145](https://github.com/GraphiteEditor/Graphite/commit/6a1a145d190887f65f1f851282bd86c8836f85a7), [commit e4d3faa](https://github.com/GraphiteEditor/Graphite/commit/e4d3faa52af42173eaf67b2dc5c2df5d6e6f23a8))</small>
- Initial [Graphene docs](/volunteer/guide/graphene) included in the contributor guide <small>([#1686](https://github.com/GraphiteEditor/Graphite/pull/1686))</small>
- Website meta tags fixed to provide better SEO and page previews for links posted on social media <small>([commit d0c493c](https://github.com/GraphiteEditor/Graphite/commit/d0c493cdb6867763d507a24f12d92f2617385114))</small>
- Redesigned website layout featuring a narrower max width and better typography <small>([#1753](https://github.com/GraphiteEditor/Graphite/pull/1753))</small>
@@ -0,0 +1,388 @@
+++
title = "Graphite progress report (Q3 2024)"
date = 2024-10-15
[extra]
banner = "https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024.avif"
banner_png = "https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024.png"
author = "Keavon Chambers & Hypercube"
summary = "Graphite's Q3 2024 update introduces improvements to performance, node graph organization, nondestructive path editing, a new render engine, and more helpful nodes."
reddit = "https://www.reddit.com/r/graphite/comments/1g4h6ya/blog_post_graphite_progress_report_q3_2024/"
twitter = "https://x.com/GraphiteEditor/status/1846283664562573344"
css = ["/component/demo-artwork.css"]
+++
[Graphite](/), a new open source 2D procedural graphics editor, has spent July–September building major improvements to **performance, node graph organization, nondestructive path editing, a new render engine, and more helpful nodes**, amongst over 100 other features and fixes.
This has been the most productive quarter yet in the project's three-year history. Most of our [Google Summer of Code](/blog/graphite-internships-announcing-participation-in-gsoc-2024/) student intern projects have already reached their goals, adding to the goodies included in this progress report. All Q3 2024 commits may be [viewed in this list](https://github.com/GraphiteEditor/Graphite/commits/master/?since=2024-07-01&until=2024-09-30) and all noteworthy changes are detailed below.
<!-- more -->
This is the third in our series of quarterly progress reports. If you missed the [first](../graphite-progress-report-q1-2024) or [second](../graphite-progress-report-q2-2024) ones, be sure to check them out as well. If you'd like to help speed up future progress, please consider [getting involved](/volunteer) with code, QA/bug testing, or art/marketing projects. [Donations](/donate) are also valued, as are [stars on GitHub](https://github.com/GraphiteEditor/Graphite). Follow along and partake in our [Discord community](https://discord.graphite.art), too.
The new *Changing Seasons* artwork shown here showcases some of the recently introduced nodes in this update. And it animates! Give it a try yourself by opening the artwork and dragging the percentage slider to morph from oak to maple leaves as the colors change.
<div class="demo-artwork">
<a href="https://editor.graphite.art/#demo/changing-seasons">
<img src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Vector art of Changing Seasons" />
</a>
<p>
<span>
<em>Changing Seasons</em>
</span>
<br />
<span>
<a href="https://editor.graphite.art/#demo/changing-seasons">Open this artwork</a> to<br />explore it yourself.
</span>
</p>
</div>
## Additions
<!--
EDITOR'S NOTE: The grammatical structure of each bullet point should follow the form: "Check out our new... [Bullet point description]"
-->
- Simplified *Boolean Operation* node that combines the best parts of the two previous boolean node versions, letting users now convert from a stack of shape layers (of any size!) into a single resulting vector shape that's more versatile to use in both the node graph and Layers panel <small>([#1813](https://github.com/GraphiteEditor/Graphite/pull/1813), [#1933](https://github.com/GraphiteEditor/Graphite/pull/1933))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/stack-based-booleans.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/stack-based-booleans.mp4" type="video/mp4" />
</video>
</div>
- *Path* node which enables nondestructive vector editing at stages along the geometry calculation pipeline, even after a procedural shape (like a star) defines the parameters that can be tweaked anytime <small>([#1676](https://github.com/GraphiteEditor/Graphite/pull/1676))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/path-node.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/path-node.mp4" type="video/mp4" />
</video>
</div>
- Additional snapping criteria for alignment and distribution between layers <small>([#1793](https://github.com/GraphiteEditor/Graphite/pull/1793))</small>
<img src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/new-snapping-modes.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Snapping choices popover menu" />
- Snapping now included with the Artboard tool for precise alignment with other artboards <small>([#1734](https://github.com/GraphiteEditor/Graphite/pull/1734))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/artboard-snapping.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/artboard-snapping.mp4" type="video/mp4" />
</video>
</div>
- Persistence of the last-chosen settings each time the Export dialog is opened (so it's not always reset to defaults), plus artboard names are now correctly written in the Bounds dropdown <small>([commit 4d3e459](https://github.com/GraphiteEditor/Graphite/commit/4d3e459f1f52f04be81727fe5946010bcabeb1eb))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/export-settings-persistence.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/export-settings-persistence.mp4" type="video/mp4" />
</video>
</div>
- Demo artwork, *Painted Dreams*, featured in the [last blog post](../graphite-progress-report-q2-2024) <small>([commit 80cf486](https://github.com/GraphiteEditor/Graphite/commit/80cf486790d9300510904622924162a9b4017b1e))</small>
<div class="demo-artwork" style="justify-content: left">
<a href="https://editor.graphite.art/#demo/painted-dreams">
<img src="https://static.graphite.art/content/blog/2024-07-31-graphite-progress-report-q2-2024.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Vector art of Painted Dreams" />
</a>
<p>
<span>
<em>Painted Dreams</em>
</span>
<br />
<span>
<a href="https://editor.graphite.art/#demo/painted-dreams">Open this artwork</a> to<br />explore it yourself.
</span>
</p>
</div>
- Quick measurement feature that is shown by holding <kbd>Alt</kbd> to momentarily see the distance between the selected and hovered shapes <small>([#1894](https://github.com/GraphiteEditor/Graphite/pull/1894))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/quick-measurement.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/quick-measurement.mp4" type="video/mp4" />
</video>
</div>
- Support for drag-and-drop and copy-paste of files more widely throughout the editor <small>([#2012](https://github.com/GraphiteEditor/Graphite/pull/2012))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/drag-and-drop-import.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/drag-and-drop-import.mp4" type="video/mp4" />
</video>
</div>
- Crash mitigation preventing node faults from taking down the whole editor, working to isolate crashes within the graph so the user can undo the change and save the file (however, reloading the editor is required to stabilize it after this occurs) <small>([#1846](https://github.com/GraphiteEditor/Graphite/pull/1846))</small>
<img src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/graph-crash-error.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Graph execution crash recovery message" />
- Seed parameters now included in all nodes with random generation <small>([commit c39032a](https://github.com/GraphiteEditor/Graphite/commit/c39032ab548d4f33d18a399c64b77d3d6f4ecd45))</small>
<img src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/node-seed-parameters.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Three nodes and their new seed parameters" />
- *Percentage Value* node for easy sliding between the numbers 0 and 100 <small>([commit d7546fb](https://github.com/GraphiteEditor/Graphite/commit/d7546fb18310490d5ce10d406e7a3faaf1ae98fe))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/percentage-value-node.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/percentage-value-node.mp4" type="video/mp4" />
</video>
</div>
- *Gradient Map* node for recoloring grayscale color values to corresponding colors along a chosen gradient <small>([commit 501b562](https://github.com/GraphiteEditor/Graphite/commit/501b562d0f17b91a54a14c83a4a0f90a1e03e369))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/gradient-map-node.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/gradient-map-node.mp4" type="video/mp4" />
</video>
</div>
- *Assign Colors* node for replacing the fill or stroke colors of individual paths in a group of vector elements using choices uniquely sampled along a gradient <small>([#1938](https://github.com/GraphiteEditor/Graphite/pull/1938))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/assign-colors-node.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/assign-colors-node.mp4" type="video/mp4" />
</video>
</div>
- *Dehaze* node for reducing the appearance of the atmospheric haze or fog in photographs <small>([#1882](https://github.com/GraphiteEditor/Graphite/pull/1882))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/dehaze-node.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/dehaze-node.mp4" type="video/mp4" />
</video>
</div>
- Node catalog reorganized with the naming and categorization of nodes given improved consistency <small>([commit e647ca9](https://github.com/GraphiteEditor/Graphite/commit/e647ca9f91a5e823137122126fe9e980f65d62ea))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/reorganized-node-catalog.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/reorganized-node-catalog.mp4" type="video/mp4" />
</video>
</div>
- *Noise Pattern* node updated to generate resolution-aware coherent noise, meaning it extends forever in all directions and zoom depths <small>([#1909](https://github.com/GraphiteEditor/Graphite/pull/1909))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/resolution-aware-noise.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/resolution-aware-noise.mp4" type="video/mp4" />
</video>
</div>
- [Dennis Kobert](https://github.com/truedoctor)'s Google Summer of Code project has concluded, adding many improvements to performance and internal improvements listed in the following sections, but also:
- Alternate render engine using [Vello](https://github.com/linebender/vello) that brings increased code simplicity and performance (especially with raster layers), but currently only works in browsers with [WebGPU support](https://developer.mozilla.org/en-US/docs/Web/API/WebGPU_API#browser_compatibility) and must be enabled via *File* > *Preferences* <small>([#1802](https://github.com/GraphiteEditor/Graphite/pull/1802), [#1865](https://github.com/GraphiteEditor/Graphite/pull/1865), [#1844](https://github.com/GraphiteEditor/Graphite/pull/1844), [#1871](https://github.com/GraphiteEditor/Graphite/pull/1871), [#1874](https://github.com/GraphiteEditor/Graphite/pull/1874), [#1875](https://github.com/GraphiteEditor/Graphite/pull/1875), [#1899](https://github.com/GraphiteEditor/Graphite/pull/1899), [#1905](https://github.com/GraphiteEditor/Graphite/pull/1905), [#1900](https://github.com/GraphiteEditor/Graphite/pull/1900), [#1907](https://github.com/GraphiteEditor/Graphite/pull/1907), [#1902](https://github.com/GraphiteEditor/Graphite/pull/1902), [#1897](https://github.com/GraphiteEditor/Graphite/pull/1897), [#1915](https://github.com/GraphiteEditor/Graphite/pull/1915), [#1996](https://github.com/GraphiteEditor/Graphite/pull/1996))</small>
<img src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/vello-preference.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Editor preferences menu with Vello setting" />
- The [final report and weekly updates](https://github.com/GraphiteEditor/Graphite/discussions/1773) are available for more details
- [Adam Gerhant](https://github.com/adamgerhant)'s Google Summer of Code project has concluded, adding several high-impact features and improvements to the node graph editing experience, including:
- Layer node chains that help keep a linear sequence of nodes organized by encapsulating them within their destination layer <small>([#1794](https://github.com/GraphiteEditor/Graphite/pull/1794), [#1812](https://github.com/GraphiteEditor/Graphite/pull/1812), [commit e28e841](https://github.com/GraphiteEditor/Graphite/commit/e28e841e3bef401c19f5ddcf01c68fedd6b5c9c3))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/node-organization-chains.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/node-organization-chains.mp4" type="video/mp4" />
</video>
</div>
- Subgraph data import/export connectors drawn along the top left/right sides of the graph, instead of the previous representation as node-looking boxes <small>([#1794](https://github.com/GraphiteEditor/Graphite/pull/1794), [commit 8e774ef](https://github.com/GraphiteEditor/Graphite/commit/8e774efe9dae51c0c1db2928e22c6de5b93d0584), [#1911](https://github.com/GraphiteEditor/Graphite/pull/1911))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/node-organization-imports-exports.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/node-organization-imports-exports.mp4" type="video/mp4" />
</video>
</div>
- Layer selection history feature, letting users go back and forth between prior states of which layers were selected using the back/forward navigation buttons on some mice (Chromium-based browsers only) or the <kbd>Alt</kbd><kbd>[</kbd> and <kbd>Alt</kbd><kbd>]</kbd> hotkeys <small>([#1945](https://github.com/GraphiteEditor/Graphite/pull/1945))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/selection-history.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/selection-history.mp4" type="video/mp4" />
</video>
</div>
- Improved layer positioning for upstream nodes when being reordered so they don't chaotically overlap or leave behind large gaps anymore <small>([#1928](https://github.com/GraphiteEditor/Graphite/pull/1928), [#1945](https://github.com/GraphiteEditor/Graphite/pull/1945))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/node-organization-reordering.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/node-organization-reordering.mp4" type="video/mp4" />
</video>
</div>
- Improved shifting of layers in stacks so they act as blocks that collide and bump other layers/nodes instead of getting stuck inside each other <small>([#1940](https://github.com/GraphiteEditor/Graphite/pull/1940))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/node-organization-block-collision.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/node-organization-block-collision.mp4" type="video/mp4" />
</video>
</div>
- [Elbert Ronnie](https://github.com/elbertronnie)'s Google Summer of Code project, building a library for decoding raw image formats, has continued and will wrap up in early November:
- Implementation of the pre-processing and demosaicing steps in the decoding pipeline <small>([#1796](https://github.com/GraphiteEditor/Graphite/pull/1796))</small>
- Implementation of the post-processing steps in the decoding pipeline <small>([#1923](https://github.com/GraphiteEditor/Graphite/pull/1923))</small>
- Factoring in of camera white balance metadata when available <small>([#1941](https://github.com/GraphiteEditor/Graphite/pull/1941))</small>
- Flipping and rotating of images based on camera orientation metadata <small>([#1954](https://github.com/GraphiteEditor/Graphite/pull/1954))</small>
- Automated tests now made to run in parallel <small>([#1968](https://github.com/GraphiteEditor/Graphite/pull/1968))</small>
- [Weekly updates](https://github.com/GraphiteEditor/Graphite/discussions/1771) are being posted while the project is ongoing, following a mid-summer hiatus
## Performance
- Caching of boolean operations enabled by fixing the Select tool's layer click targets robustly despite the boolean node altering the shape data <small>([commit 6ecb173](https://github.com/GraphiteEditor/Graphite/commit/6ecb173c1c06807f13a859ef90b7d7f43af042be), [#1946](https://github.com/GraphiteEditor/Graphite/pull/1946), [#1986](https://github.com/GraphiteEditor/Graphite/pull/1986))</small>
- Rewritten boolean operations algorithm that runs purely in Rust instead of making high-overhead calls into a JavaScript library <small>([#1952](https://github.com/GraphiteEditor/Graphite/pull/1952), [#2000](https://github.com/GraphiteEditor/Graphite/pull/2000))</small>
- Internal hash-based data tracking now benefits from caching of hash calculations <small>([#1876](https://github.com/GraphiteEditor/Graphite/pull/1876))</small>
- Refactored document node type lookup process that fixes performance degradation over time <small>([#1878](https://github.com/GraphiteEditor/Graphite/pull/1878))</small>
- Speed-ups to the node graph compilation that must occur after every change before it's rendered <small>([#1924](https://github.com/GraphiteEditor/Graphite/pull/1924))</small>
- Usage of a coarse bounding box when clicking on layers to avoid a detailed check for intersection <small>([#1887](https://github.com/GraphiteEditor/Graphite/pull/1887))</small>
## Fixes
- Fix for rulers showing in the wrong spot when initially opening a document <small>([#1801](https://github.com/GraphiteEditor/Graphite/pull/1801))</small>
- Fix for backwards compatibility broken in a prior change <small>([commit 621f469](https://github.com/GraphiteEditor/Graphite/commit/621f469a150d4a14c86ba9be87df82aae5dded74))</small>
- Fix for boolean operations containing multiple subpaths <small>([#1804](https://github.com/GraphiteEditor/Graphite/pull/1804))</small>
- Fix for a bug causing some node outputs to fail connecting wires to other inputs <small>([commit 84d7262](https://github.com/GraphiteEditor/Graphite/commit/84d72621e545ba7743e38d46ce1607133bb13461))</small>
- Fix for the 'Zoom with Scroll' preference not working after a page refresh <small>([#1807](https://github.com/GraphiteEditor/Graphite/pull/1807))</small>
- Fix for broken extending of paths in the Pen and Freehand tools <small>([#1809](https://github.com/GraphiteEditor/Graphite/pull/1809))</small>
- Fix for a snapping bug where snapped-together shapes couldn't be moved <small>([#1818](https://github.com/GraphiteEditor/Graphite/pull/1818))</small>
- Fix for transforms in groups not being pre-applied in the boolean node <small>([#1825](https://github.com/GraphiteEditor/Graphite/pull/1825))</small>
- Fix for dragging groups so the transform node is inserted as intended <small>([#1829](https://github.com/GraphiteEditor/Graphite/pull/1829))</small>
- Fix for restoring older prototype GPU infrastructure functionality to work again <small>([#1797](https://github.com/GraphiteEditor/Graphite/pull/1797))</small>
- Fix for a breakage to shallow select mode in the Select tool <small>([#1843](https://github.com/GraphiteEditor/Graphite/pull/1843))</small>
- Fix for the graph not being updated in the UI after using <kbd>Ctrl</kbd><kbd>D</kbd> to duplicate a node <small>([#1852](https://github.com/GraphiteEditor/Graphite/pull/1852))</small>
- Fix for the Layers panel UI not updating when the selection changes or a layer is expanded/collapsed <small>([#1847](https://github.com/GraphiteEditor/Graphite/pull/1847))</small>
- Fix for the rendering of non-closed paths and additional improvements to layer bounding box calculation <small>([#1859](https://github.com/GraphiteEditor/Graphite/pull/1859))</small>
- Fix for how documents are saved with images now correctly serialized in linear space instead of sRGB <small>([#1856](https://github.com/GraphiteEditor/Graphite/pull/1856))</small>
- Fix for the grid overlay color choice that was incorrectly appearing as no color <small>([commit a4a5139](https://github.com/GraphiteEditor/Graphite/commit/a4a513911dac29ee5c6546842be194b1513f2cec))</small>
- Fix for viewport bounds getting out of sync at times, like when toggling rulers <small>([commit 0617759](https://github.com/GraphiteEditor/Graphite/commit/06177597ae8ef52f7a273875d6afb63fb09ec3c8))</small>
- Fix for compatibility issues with rendered SVG output syntax <small>([#1880](https://github.com/GraphiteEditor/Graphite/pull/1880))</small>
- Fix for node graph type errors that were not being shown <small>([#1917](https://github.com/GraphiteEditor/Graphite/pull/1917))</small>
- Fix to add colors to all nodes in a graph, even if disconnected, and properly display hidden network imports <small>([#1921](https://github.com/GraphiteEditor/Graphite/pull/1921))</small>
- Fix to respect the "Clip" parameter on Artboards when exporting "All Artworks" <small>([#1916](https://github.com/GraphiteEditor/Graphite/pull/1916))</small>
- Fix to improve the undo/redo history system's robustness <small>([#1945](https://github.com/GraphiteEditor/Graphite/pull/1945))</small>
- Fix for a crash caused by gradients with bounds of zero <small>([#1950](https://github.com/GraphiteEditor/Graphite/pull/1950))</small>
- Fix for SVG importing and exporting which had both broken <small>([#1953](https://github.com/GraphiteEditor/Graphite/pull/1953))</small>
- Fix for a crash in our Bezier-rs library when outlining a small path <small>([#1958](https://github.com/GraphiteEditor/Graphite/pull/1958))</small>
- Fix for a crash due to an invalid segment when dissolving a vector path point loop <small>([#1959](https://github.com/GraphiteEditor/Graphite/pull/1959))</small>
- Fix to improve how integer ruler intervals are set when zoomed in <small>([#1966](https://github.com/GraphiteEditor/Graphite/pull/1966))</small>
- Fix for layer stacks inadvertently producing a 0x0 image because of an incorrect default type for the disconnected bottom layer node input <small>([#1965](https://github.com/GraphiteEditor/Graphite/pull/1965))</small>
- Fix to make the primitive shape tools avoid setting a negative transform scale <small>([#1973](https://github.com/GraphiteEditor/Graphite/pull/1973))</small>
- Fix to nudge only the shallowest selected layers to avoid amplified translation <small>([#1975](https://github.com/GraphiteEditor/Graphite/pull/1975))</small>
- Fix to the Select tool's click targets which had extended too far on narrow layers <small>([#1976](https://github.com/GraphiteEditor/Graphite/pull/1976))</small>
- Fix to correctly apply transforms to vector data and strokes <small>([#1977](https://github.com/GraphiteEditor/Graphite/pull/1977))</small>
- Fix for the types imported into subgraphs <small>([#1978](https://github.com/GraphiteEditor/Graphite/pull/1978))</small>
- Fix to now properly use overlays for drawing artboard names in the viewport <small>([#1981](https://github.com/GraphiteEditor/Graphite/pull/1981))</small>
- Fix for broken Opacity slider in Layers panel <small>([#2004](https://github.com/GraphiteEditor/Graphite/pull/2004))</small>
## Internal
- Refactor for graph editing to go through a new abstraction layer, the node network interface <small>([#1794](https://github.com/GraphiteEditor/Graphite/pull/1794), [#1906](https://github.com/GraphiteEditor/Graphite/pull/1906))</small>
- Addition of a new Scope API for exposing data within graphs and subgraphs <small>([#1814](https://github.com/GraphiteEditor/Graphite/pull/1814))</small>
- Reorganization and upgrading of most Cargo dependencies <small>([#1815](https://github.com/GraphiteEditor/Graphite/pull/1815))</small>
- Removal of artboards as a type of graphical element, which is no longer in use as such <small>([#1824](https://github.com/GraphiteEditor/Graphite/pull/1824), [#1830](https://github.com/GraphiteEditor/Graphite/pull/1830))</small>
- Cleanup of many Clippy warnings <small>([#1808](https://github.com/GraphiteEditor/Graphite/pull/1808), [#1936](https://github.com/GraphiteEditor/Graphite/pull/1936), [#1995](https://github.com/GraphiteEditor/Graphite/pull/1995))</small>
- Build process simplifications by auto-installing npm packages and reducing the contributor guide's project setup complexity <small>([#1799](https://github.com/GraphiteEditor/Graphite/pull/1799))</small>
- Fixes for Rust-Analyzer conflicts with build targets and other compile speed issues <small>([commit 857bc77](https://github.com/GraphiteEditor/Graphite/commit/857bc772de8c5731a2eaf7f442beb573ccf8ca4c))</small>
- Fixes, documentation, and updates for npm dependencies and tooling, as well as fixes for Bezier-rs demos not building <small>([#1857](https://github.com/GraphiteEditor/Graphite/pull/1857))</small>
- Profiling metrics development infrastructure for Graphene graph compilation <small>([#1924](https://github.com/GraphiteEditor/Graphite/pull/1924), [#1974](https://github.com/GraphiteEditor/Graphite/pull/1974))</small>
- CI infrastructure to post Clippy warnings as PR comments <small>([#1926](https://github.com/GraphiteEditor/Graphite/pull/1926), [commit a93dcb2](https://github.com/GraphiteEditor/Graphite/commit/a93dcb2776027bfcb385ae9dfddff249bdfd896b))</small>
- CI infrastructure which profiles the demo artwork test documents and comments on PRs that induce sizable changes in performance <small>([#1925](https://github.com/GraphiteEditor/Graphite/pull/1925), [#1939](https://github.com/GraphiteEditor/Graphite/pull/1939))</small>
- CI infrastructure to post a ZIP of self-hosted build assets for the [latest editor release tag on GitHub](https://github.com/GraphiteEditor/Graphite/releases/tag/latest-stable) <small>([commit f2493d5](https://github.com/GraphiteEditor/Graphite/commit/f2493d5308ace728c3525d51609c3a2efcbdc139))</small>
- Refactor to support node ID path insertion at compile time <small>([#1947](https://github.com/GraphiteEditor/Graphite/pull/1947))</small>
- Switch of attribute-based vector data from referencing point IDs to indexes in the points table <small>([#1949](https://github.com/GraphiteEditor/Graphite/pull/1949))</small>
- Refactor for the navigation metadata that stores the node graph UI view's current pan and zoom <small>([#1956](https://github.com/GraphiteEditor/Graphite/pull/1956))</small>
- Refactor for the node definition syntax to make it easier, shorter, and more consistent to define nodes across the board <small>([#1942](https://github.com/GraphiteEditor/Graphite/pull/1942), [#1999](https://github.com/GraphiteEditor/Graphite/pull/1999), [#2002](https://github.com/GraphiteEditor/Graphite/pull/2002))</small>
- Fix for a crash when using the experimental Graphene CLI tool <small>([#1993](https://github.com/GraphiteEditor/Graphite/pull/1993))</small>
- Upgrades to quality of third-party library license notice generation <small>([commit 14de67c](https://github.com/GraphiteEditor/Graphite/commit/14de67c5a78a1e8369a8b023da441237a44e48cb))</small>
- Cleanup for various TypeScript code errors and additions to CI infrastructure to enforce TS error checking <small>([commit 1ee5ffb](https://github.com/GraphiteEditor/Graphite/commit/1ee5ffbbe8a64da5a83da889dd16d41c3463c332))</small>
- Simplifications to the [Bezier-rs web demos](https://keavon.github.io/Bezier-rs/) to reduce the bundle size and streamline its code <small>([commit 0b0169a](https://github.com/GraphiteEditor/Graphite/commit/0b0169a415453b9d9910d765f0538ecd67b618c0))</small>
- Manually-runnable benchmarks for document runtime speed profiling <small>([#2005](https://github.com/GraphiteEditor/Graphite/pull/2005))</small>
## Website
- Revamps to the website <small>([commit 5d74178](https://github.com/GraphiteEditor/Graphite/commit/5d74178f5f417afdd70abec596d38f22d490240a), [commit 40fd447](https://github.com/GraphiteEditor/Graphite/commit/40fd4473a784ba24fc3105f8da56baacccf2dcf5), [commit 98ab069](https://github.com/GraphiteEditor/Graphite/commit/98ab069a1703e89cea031fedffc55c3bf4191b5d))</small>
- Updates to various content on the website <small>([commit fb7d597](https://github.com/GraphiteEditor/Graphite/commit/fb7d5970b38c61fc1a35aeefd7028858c96f5ff2))</small>
## Announcements
### Graphite @ Maker Faire
<img src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/maker-faire-banner.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.jpg')" alt="Poster for the Bay Area Maker Faire" />
Graphite will have a booth at the [Bay Area Maker Faire](https://makerfaire.com/bay-area/) **this weekend, October 18–20**. If you're in northern California, come visit and meet our team. We'll be located in Coal Shed 2 and plan to showcase the latest features, answer questions, teach how to use the app, get to know the maker community, and give away stickers and art postcards. We hope to see you there!
### Nodevember
<img src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/nodevember-logo.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Poster for the Bay Area Maker Faire" />
We're excited to be participating in [Nodevember](https://nodevember.io/) next month! The event celebrates procedural art and design with themed prompts throughout the month. We're looking forward to seeing what the community creates with Graphite's nodes. We'll be sharing some of our favorite submissions on our social media channels. Be sure to tag `@GraphiteEditor` and use the `#MadeWithGraphite` hashtag together with `#Nodevember2024`.
### Community art contest
Congratulations to [VDawg](https://www.instagram.com/vdawg.jpg/), the winner of the community art contest, whose work is featured below.
<img src="https://static.graphite.art/content/blog/2024-10-15-graphite-progress-report-q3-2024/art-contest-winner.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.jpg')" alt="Winning artwork depicts a dark landscape with an ethereal diamond forming a constellation with the stars above" />
@@ -0,0 +1,243 @@
+++
title = "Year in review: 2024 highlights and a peek at 2025"
date = 2025-01-16
[extra]
banner = "https://static.graphite.art/content/blog/2025-01-16-year-in-review-2024-highlights-and-a-peek-at-2025.avif"
banner_png = "https://static.graphite.art/content/blog/2025-01-16-year-in-review-2024-highlights-and-a-peek-at-2025.png"
author = "Keavon Chambers"
summary = "Graphite has come a long way in 2024. Read about the progress made and the plans for the upcoming year."
reddit = "https://www.reddit.com/r/graphite/comments/1i3umnl/blog_post_year_in_review_2024_highlights_and_a/"
twitter = "https://x.com/GraphiteEditor/status/1880404337345851612"
bluesky = "https://bsky.app/profile/graphiteeditor.bsky.social/post/3lfxysayh622g"
js = ["/js/component/youtube-embed.js"]
css = ["/component/youtube-embed.css"]
+++
Another year has come and gone which has propelled Graphite—further than any year before—towards the ambitious goal of satiating the open source community's expanding appetite for an awesome 2D content creation suite that surpasses established choices in ease-of-use, powerful features, and affordability (at the unbeatable price of *free*).
<!-- more -->
In a world where the notion of software ownership seems headed towards extinction, the need has never been greater for an independent, community-built alternative to the vector graphics, animation, image manipulation, photo processing, and publishing tools used daily by millions of creators worldwide.
Graphite is and will always remain yours to keep, whether that's by running the lightweight, client-side [web app](https://editor.graphite.art) (no signup, no cloud), <a href="https://support.google.com/chrome/answer/9658361" target="_blank">installing the PWA</a> on your desktop, self-hosting the <a href="https://github.com/GraphiteEditor/Graphite/releases/tag/latest-stable" target="_blank">builds</a>, or downloading the soon-to-be-ready native app for your OS of choice (more news on that later in the post).
<style class="float-image">
.float-image + p {
text-align: left;
}
.float-image + p > a {
float: right;
margin-left: 1.5em;
margin-bottom: 1em;
}
</style>
<p>
<a href="https://github.com/GraphiteEditor/Graphite"><img src="https://static.graphite.art/content/blog/2025-01-16-year-in-review-2024-highlights-and-a-peek-at-2025/10k-stars.avif" style="max-width: unset; margin-top: 0.5em" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Screenshot of 10,000 stars" /></a>
<span>Join me, founder and designer of Graphite, to see where the past year has brought us on this quest. And let me take this moment to thank our growing community for sharing my vision and showing support, both <a href="/donate">financially</a> and by boosting the GitHub project page over the 10,000 star milestone just in time to celebrate the end of a productive 2024.</span>
</p>
## 2024 development progress report
<p><img src="https://static.graphite.art/content/blog/2025-01-16-year-in-review-2024-highlights-and-a-peek-at-2025/weekly-commit-rate.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="" /></p>
<center><em>Weekly code commit rate in 2024</em></center>
In 2024, Graphite grew from a promising tech demo into a by-and-large useful application for vector artistry and graphic design, equipped with its totally unique secret ingredient: nondestructive, procedural editing via a node graph. This was a year focused on iterating until a prototype became a polished product— at least by the standards of alpha-stage software. If you haven't [tried Graphite](https://editor.graphite.art) recently, please take another look!
Improvements made throughout Alpha 3 (2024's release series) brought the formerly abysmal performance up to now-adequate levels and solved the vast bulk of instability with the once-numerous crashes and bugs. Advancements to [Graphene](/volunteer/guide/graphene), our bespoke node graph engine technology, has let us begin to support new rendering possibilities, introduce more helpful nodes, and remove restrictive limitations with common node combinations that previously were points of frustration. We also made big strides improving the tools used by artists for vector drawing with features like boolean path operations, snapping, layer selection history, quick measurement, gradient picking, and extensive usability-focused tweaks—both big and small—all throughout the editor.
This was also the first year of publishing quarterly development reports to the blog. We have aimed to keep them all visually interesting by showing the new features with looping video clip demonstrations alongside digestible sentence-long change summaries. Please let us know if you find this format valuable and worth continuing in 2025.
- [Graphite progress report (Q1 2024)](../graphite-progress-report-q1-2024)
- [Graphite progress report (Q2 2024)](../graphite-progress-report-q2-2024)
- [Graphite progress report (Q3 2024)](../graphite-progress-report-q3-2024)
- Graphite progress report (Q4 2024) will be published soon— stay up-to-date with the [newsletter](/#newsletter) or [RSS feed](../rss.xml)
## Alpha roadmap update
Alpha 3 began back in February 2024 and we plan to declare the start of the Alpha 4 release series again in February after wrapping up the holiday development cycle and shifting gears for the big projects we plan to tackle in 2025. I'll expand on those goals for the upcoming year later in this article. If development keeps pace with plans, Alpha 4 may be the last before Beta begins as early as the start of 2026.
## Google Summer of Code results
<p><img src="https://static.graphite.art/content/blog/2025-01-16-year-in-review-2024-highlights-and-a-peek-at-2025/gsoc-logo.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Google Summer of Code program logo" /></p>
<center><em>Read our short <a href="../graphite-internships-announcing-participation-in-gsoc-2024">blog post</a> about participating in Google Summer of Code</em></center>
Our project had the fortune of being selected to participate for its first year as a mentoring organization in <a href="https://summerofcode.withgoogle.com/archive/2024/organizations/graphite" target="_blank">Google Summer of Code</a> (GSoC). We were given the opportunity to welcome three student interns to the team as they developed significant contributions to our open source code base throughout the summer.
I would like to express my gratitude to Google for funding the program and its stipends given to our students. It has ushered in a great deal of talent to our contributor community. And even beginning right now, we are seeing proactive prospective students begin [submitting code in anticipation](../graphite-internships-announcing-participation-in-gsoc-2024) of the 2025 program. We hope our organization is invited to return for the opportunity to mentor another cohort of budding talent, much like our 2024 students—Adam, Elbert, and Dennis—whose accomplishments far surpassed my most optimistic expectations.
<a href="https://github.com/GraphiteEditor/Graphite/discussions/1769" target="_blank">Adam contributed</a> an extensive evolution of Graphite's node editor. He introduced adjustment layers, editable nested graphs, and layer/node organization features that have upgraded the capabilities of the procedural editing environment, bringing it closer to my product vision. I'm delighted to announce that Adam has decided to stick around past the summer and join the [core team](/about#core-team). His continued development of the node editing systems to fully realize my design goals will make the editor magnitudes more powerful, and at the same time, easier to use. More on those plans later in the article.
<a href="https://github.com/GraphiteEditor/Graphite/discussions/1771" target="_blank">Elbert built</a> a new Rust library, <a href="https://crates.io/crates/rawkit" target="_blank">Rawkit</a>, for decoding and processing `.arw` files from Sony digital cameras, one of the most popular camera brands. Nikon, Canon, and someday all other formats are in scope for the future direction of the code. This library gives us the fine-grained control we require and will allow Graphite to begin its focus on digital photo processing in 2025 once several technical limitations are overcome and Rawkit is integrated into Graphite.
<a href="https://github.com/GraphiteEditor/Graphite/discussions/1773" target="_blank">Dennis developed</a> a range of critical improvements around the theme of performance. Before his contributions, working in Graphite was too slow for practical usability. Afterwards, it became fast enough to be useful in most scenarios (except when dealing with excessive complexity in terms of pixel or vector point counts— those known bottlenecks remain as future work). He made rendering and various nodes such as boolean operations faster, built profiling tools to aid in the continued quest for speed, and made changes adding to the robustness of the entire node engine. Lastly, he integrated the <a href="https://github.com/linebender/vello" target="_blank">Vello</a> high-performance vector graphics renderer to replace our SVG-based rendering method. This streamlines the rasterization and compositing that's involved in showing you the artwork in your viewport. Vello can be turned on from the editor preferences menu and will be enabled by default later in 2025 when browser support for the WebGPU API, which Vello relies on, becomes widespread.
Another part of the GSoC program was the Mentor Summit in October where I got to meet and share knowledge with open source maintainers from the other participating organizations. Hosted at Google's Mountain View offices, it was a weekend of talking tech and learning the lessons of open source project management from the seasoned veterans.
<p><img src="https://static.graphite.art/content/blog/2025-01-16-year-in-review-2024-highlights-and-a-peek-at-2025/gsoc-mentor-summit-photo.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.jpg')" alt="" /></p>
<center><em>Group photo of the GSoC Mentor Summit attendees</em></center>
<p><img src="https://static.graphite.art/content/blog/2025-01-16-year-in-review-2024-highlights-and-a-peek-at-2025/gsoc-mentor-summit-session-board.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.jpg')" alt="" /></p>
<center><em>Schedule board highlighting my session on day 2</em></center>
But I also managed to pass on knowledge in a subject I'm passionate about: design. I led a session titled "Well-Designed User Interfaces in FOSS Apps" in which 30-40 participants came to explore and discuss how the open source community can improve in this notoriously challenging discipline. The Mentor Summit was just one of the community events we took part in over this past year.
## Community events
Outside of my daily routine spent coding, designing, and managing the project, I've also continued working to expand our community engagement and industry outreach. Members of the Graphite team met up at events during the year to represent the project and plant the seeds of future growth for our mission beyond the reach of the little pocket of the internet we call home.
### Game Developers Conference
In March, accompanied by Oliver Davies, a personal friend/tech artist/contributor to Graphite's product design, he and I visited the <a href="https://gdconf.com/" target="_blank">Game Developers Conference</a> (GDC) in San Francisco for opportunities to meet face-to-face with industry colleagues, as we did also the past two years.
We introduced Graphite to open source organizations like <a href="https://godotengine.org/" target="_blank">Godot</a> and <a href="https://o3de.org/" target="_blank">O3DE</a>, caught up with Francesco and Dalai from the Blender Foundation, joined a roundtable panel on open source adoption in the games industry, and came together with the <a href="https://gamedev.rs/" target="_blank">Rust Gamedev</a> community for a physical meetup of game and graphics centric Rust developers. It was a treat seeing several people in-person for the first time whom, before, I'd known only online.
<p class="wide"><img src="https://static.graphite.art/content/blog/2025-01-16-year-in-review-2024-highlights-and-a-peek-at-2025/gdc-collage.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.jpg')" alt="" /></p>
<center><em>
Upper-left: Forest Anderson, host of the Rust Gamedev Meetup <a href="https://www.youtube.com/watch?v=Ea4Wt_FgEEw&list=PLYiOdhpKxxXI9l8V15FciLcsPzkz3Gt4I&index=2" target="_blank">live streams</a> where Graphite demoed many monthly development milestones, with me at the Rust devs hangout;
Upper-right: roundtable panel on open source adoption in the games industry;
Lower-left: Godot's presence at GDC;
Lower-right: Francesco Siddi (1) and Dalai Felinto (2) from the Blender Foundation with me (3) and Oliver (4)
</em></center>
With March now just around the corner, I am definitely looking forward to the next conference. If you will be in town for GDC 2025 and would like to meet up, please [get in touch](/contact).
### Blender Conference LA
<p class="wide"><img src="https://static.graphite.art/content/blog/2025-01-16-year-in-review-2024-highlights-and-a-peek-at-2025/bcon-la-collage.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.jpg')" alt="" /></p>
<center><em>Left-to-right, top-then-bottom: Ton Roosendaal (founder of Blender) and me (founder of Graphite); the venue on Hollywood Boulevard; talks on the main stage; Colin Levy (director of <a href="https://www.youtube.com/watch?v=Mv30ExfoKcc" target="_blank">Skywatch</a> and the Blender Studio's <a href="https://www.youtube.com/watch?v=eRsGyueVLvQ" target="_blank">Sintel</a>) and Andrew Price (<a href="https://www.youtube.com/@blenderguru" target="_blank">Blender Guru</a> and creator of <a href="https://www.poliigon.com/" target="_blank">Poliigon</a>); my talk on the main stage; Alan Melikdjanian (<a href="https://www.youtube.com/user/CaptainDisillusion" target="_blank">Captain Disillusion</a>) and <a href="https://www.youtube.com/@IanHubert2" target="_blank">Ian Hubert</a> (YouTube filmmaker and director of the Blender Studio's <a href="https://www.youtube.com/watch?v=41hv2tW5Lc4" target="_blank">Tears of Steel</a>); attendees chatting</em></center>
The next month in April, Oliver and I went to our second conference of the year: <a href="https://bconla.org/2024/" target="_blank">BCON LA</a>, the first Blender Conference held in Los Angeles. We connected again with the Blender team and met many 3D/VFX industry professionals and prominent members of the Blender community over the two days of the event.
I also took to the main stage to present a lightning talk introducing Graphite to the community:
{{ youtube_embed(id="x3P5eYv11EU", timestamp=1603, title="BCON LA 2024 - Lightning Talks") }}
### Graphite booth at Open Sauce
The month of June was particularly special because of <a href="https://opensauce.com/" target="_blank">Open Sauce</a>, a convention and expo in San Francisco for makers and creators, and of course, open source projects!
<p><img src="https://static.graphite.art/content/blog/2025-01-16-year-in-review-2024-highlights-and-a-peek-at-2025/open-sauce-booth.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.jpg')" alt="" /></p>
<center><em>Adam and Oliver driving a Graphite live demo at our booth</em></center>
This presented the perfect opportunity to host our own exhibitor booth and talk to hundreds of excited attendees with creative tech backgrounds over the two day show. The event was excellent for networking with fellow makers and a chance to meet an array of the guest YouTube creators including several from the digital content creation realm.
<p class="wide"><img src="https://static.graphite.art/content/blog/2025-01-16-year-in-review-2024-highlights-and-a-peek-at-2025/open-sauce-collage.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.jpg')" alt="" /></p>
<center><em>Sides: doodles by attendees on our public art wall; Inner-left: <a href="https://www.youtube.com/@IanHubert2" target="_blank">Ian Hubert</a>, Blender filmmaker, visiting again after we met at BCON LA; Inner-right: Daniel Shiffman (<a href="https://www.youtube.com/@TheCodingTrain" target="_blank">The Coding Train</a>), creator of tutorials and explorations into creative coding/generative procedural art</em></center>
I designed the booth with the goal of becoming an inviting artist's space. Visitors could contribute doodles to the pair of LED-backlit dry erase boards, walk inside to talk with us about the project, and sit down to explore the app. This was a valuable chance to "playtest" the user experience with a steady supply of new people from a variety of backgrounds. I also learned how to refine our approach to communicating clearly what the product is and does.
Joining me again was Oliver to assist with the booth, as well as our new GSoC contributor, Adam, who flew up from southern California to help. In between the hustle and bustle, we put the face-to-face time to good use communicating the vision and planning many aspects of his node graph development.
### Graphite booth at the Bay Area Maker Faire
And then when October rolled around, we did it again! Now located at a post-industrial waterfront venue across the Bay from San Francisco, the <a href="https://makerfaire.com/bay-area/" target="_blank">Maker Faire</a> started with a Friday field trip day for local schools followed by a full weekend of general attendance.
<p><img src="https://static.graphite.art/content/blog/2025-01-16-year-in-review-2024-highlights-and-a-peek-at-2025/maker-faire-booth.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.jpg')" alt="" /></p>
<center><em>Visitors making art and learning about Graphite at our booth</em></center>
The Maker Faire is ground zero for the Maker Movement. I grew up going each year as a kid. It was an era when consumer 3D printing was completely new and that was the only place one could discover—and obsess over—the technology. The Faire influenced my career path into engineering and the arts, so it's fitting that I would grow up to return and share an open source project, born out of that community spirit, for the next generation of creative young minds.
Attracting a more family-oriented audience than Open Sauce, it presented the chance to learn how approachable Graphite is even for kids. Doodling on the LED-backlit whiteboards flanking our booth was especially popular with that age range and brought in many passers-by. Of those who tried Graphite, I was blown away to see how some of our youngest visitors—down to the age of 6—were also the most capable and engaged using the product, diving in deep with barely any instruction. As the UI and product designer, this assured me that I have been on the right track so far. I believe now more than ever that my ambitious goal is achievable: creating the most intuitive and user-friendly professional graphics editor on the market.
<p><img src="https://static.graphite.art/content/blog/2025-01-16-year-in-review-2024-highlights-and-a-peek-at-2025/maker-faire-demo-stations.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.jpg')" alt="" /></p>
<center><em>All four demo computers occupied with Graphite's newest users</em></center>
Another exciting part of the Maker Faire experience was bringing together nearly the full Graphite core team in-person. Oliver and Adam came to help again while we were also joined by Dennis visiting all the way from Germany, conveniently coinciding with a vacation he had planned. We put our commute time towards deeply technical code architecture discussions and knowledge transfer.
<p class="wide"><img src="https://static.graphite.art/content/blog/2025-01-16-year-in-review-2024-highlights-and-a-peek-at-2025/maker-faire-team-collage.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.jpg')" alt="" /></p>
<center><em>Left: me (1), Adam (2), and Oliver (3) at the venue; Right: Adam (1), Dennis (2), and me (3) (Oliver and Dennis couldn't make it on the same day)</em></center>
Graphite is tentatively anticipating a return for Open Sauce and the Maker Faire again in 2025. Make plans to come and visit!
## Looking ahead to 2025
There are so many plans that I'm eager to carry out to improve the clarity and capability of the experience Graphite offers our users. Some are small, and a few we will be working on for most of the year.
### Desktop app
Starting out with our most in-demand request: a desktop app. This has been on our roadmap from the start but only recently it's begun making sense putting it at the front of the roadmap priorities. We hoped to complete it by the end of 2024, but that wasn't in the cards due to developer availability and the specialized skills needed to complete the task.
Now to get technical, the lazy option exists: chucking the whole web app in an unaltered Electron wrapper, but this is a technological dead end that I believe offers no value compared to a PWA. The value comes from offering an actual native app where the editor Rust code and GPU-accelerated rendering runs on a user's Windows, Mac, or Linux machine without browser overhead. Our use case of combining the web-rendered editor interface with the user's native-rendered artwork presents several unique challenges that our team has to overcome— individually on each platform. If you have experience with native development on Windows, Mac, and/or Linux, please get involved to speed up this effort! With our current resources, I am anticipating this will be ready for release around spring.
### Animation
Next, the feature I am personally most itching to dive into developing is animation. I've been recently iterating on the UI design mockup for the Timeline panel which will support keyframing any desired node parameters. This new panel will seamlessly integrate into the existing graph-based, data-driven workflow and make it easy to create motion graphics paired with procedural generation.
<p class="wide"><img src="https://static.graphite.art/content/blog/2025-01-16-year-in-review-2024-highlights-and-a-peek-at-2025/timeline-panel-ui-mockup.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="" /></p>
<center><em>User interface mockup for the latest animation panel plans prior to being implemented</em></center>
### Advanced procedural editing
It is now becoming time to delve into the next phase of making the node graph more powerful by introducing several key features:
- Lambdas: treating a node as a piece of data given to another node, so it can be run in a loop with varying parameters in each iteration.
- Instances: generalizing graphical data, transforms, and groups so that every layer is one or multiple instances, each with a unique transform. This will finally fix the long-lived limitation of layers lacking a proper pivot point.
- Lists: representing lists of data like vector points and segments in a spreadsheet. Formalizing the tabular data representation lets the node engine benefit from ECS-like performance gains by optimizing CPU cache utilization.
- Attributes: encoding properties (of points, of segments, of instances, of appearance styles, etc.) in columns on the tabular data. This will unlock Graphite to become as powerful as Blender geometry nodes which works based on the same design principle.
### Raster graphics editing
Graphite has included a primitive kind of raster support for a while, mostly used for including reference images when creating vector content. Some raster nodes can adjust the colors of images, but there are no tools yet for selecting and drawing over parts of an image to make localized edits. Furthermore, CPU-centric bottlenecks slow down the editor when big images are in use. The GPU is not used by any nodes operating on pixel-based data.
Consequently, raster editing just isn't viable yet until tool and GPU node support arrives. The innumerable complexities would not fit in this section, but if you have a background in compilers or graphics programming, please hop on <a href="https://discord.graphite.art" target="_blank">our Discord</a> and ask about it if you're curious or potentially interested in helping. After the infrastructure parts are in place, we can begin building nodes that make localized edits (such as a masking node) and start developing tools including a fully rewritten brush engine and a mode for drawing marquee selections. I can't yet predict how far we will get by year's end with these interactive localized editing tools because it will all depend on how quickly the prerequisite technical infrastructure components come together. Part of that will depend on how soon the browser vendors ship universal <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebGPU_API#browser_compatibility" target="_blank">WebGPU API support</a> so it can be deployed beyond an experimental state in Graphite.
### Graphite in education
A valuable discovery came out of exhibiting Graphite at the Maker Faire. I had conversations with several school teachers who were interested in using Graphite in their classrooms.
I was told that other web-based graphics editors are commonly blocked by the IT admins of school networks due to policies against visiting sites with ads. (Crazy!) Since Graphite is entirely ad-free and runs on Chromebooks, this presents an opportunity to focus on better supporting the education market. In fact, I have recently been watching Chromebooks account for a small but rapidly growing portion of our site's visitors which means some educators are teaching Graphite in their classrooms:
<p><img src="https://static.graphite.art/content/blog/2025-01-16-year-in-review-2024-highlights-and-a-peek-at-2025/school-usage-trends.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="" /></p>
<center><em>Graphite visitors on Chromebooks have trended up this school year but dropped down to background levels during summer and winter breaks, hinting at usage in classrooms</em></center>
This was previously a user demographic that I didn't consider. My school experience never offered graphic design instruction. I had to teach it to myself, and when using school computers, I ran portable installations of remote desktop software to access the desktop creative applications on my home computer. That experience led to my selection of a software stack that would support web-based access to the Graphite editor for the sake of students like my past self, but my previous assumption was that teacher-led instruction would be years away.
Learning that there are educators who want to teach these skills directly to students was eye-opening. It could be an excellent fit: each teacher brings along dozens of users. A teacher can learn the app once and disseminate the instruction which saves us from needing pristine learning resources at this early stage. Then a teacher can find the most common sources of confusion and filter that feedback back to us for improvement. Compared to professional artists who can't always justify using alpha software, students are a less demanding type of user. And Graphite benefits from a generation of students growing up to continue using the app. It seems like a surprisingly good fit.
My goal in 2025 is to begin prioritizing specific resources for educators that might include:
- Pushing for development efforts that will improve performance to help the app run better on low-spec hardware. This benefits everyone else just as greatly!
- Reducing common pitfalls in the software that are especially likely to be encountered by inexperienced users.
- Putting more time towards creating learning resources and documentation to help instructors learn Graphite well enough to teach it and solve student issues.
- Collaborating with teachers to devise and develop a curriculum package that can be used in classrooms to teach specific skills.
- Creating an information page for educators to discover the project, learn how it suits their needs, access curriculum, and get connected with us.
If you are a teacher, or know one, who would be interested in adding this manner of STEM/STEAM instruction to your classroom, please [get in touch](/contact) so we can figure this out together.
## Thank you for helping us help you help us all
By not being backed by investors or built by staff engineers or marketed by an agency, Graphite is an ambition that is constantly treading the line that borders the realm of impossibility. But thanks to your support, I am confident our efforts will prevail. 2025 is the year when all the pieces fall into place with a desktop app, competitive performance, features, and raster image editing. It will be the free software you can own (and love) that holds up against the software you have no choice but to rent (and put up with as it trains AI on your private work).
Ultimately, reaching critical mass might take one year. It might take five. That part is up to you. Momentum and resources are both scarce which means you, personally, have the opportunity to make an outsized impact.
If you choose to [become a member](/donate), you are directly helping fund our expenses like conference travel and ordering T-shirts that keeps the volunteer team happy and motivated to code. Remember that "free software" doesn't mean it's free to produce, it just means someone else is paying for it if you aren't pitching in. We just added an option for [donating directly](/donate#supporter-memberships) without needing a GitHub account, so now it's easier than ever to contribute.
If you choose to [volunteer](/volunteer), you lift our greatest bottleneck—time—and bring your unique skills to the table. There are opportunities from coding to technical writing to art, design, and marketing. It's a team effort, but only if there's a team to delegate the efforts to.
And there are other ways to help out. Sign up as a QA tester in our <a href="https://discord.graphite.art" target="_blank">Discord</a>. Make it your mission to share Graphite by word-of-mouth on the forums and online communities you frequent. Put it on the radar of the creators you follow. Create and post your own tutorials on the web. Use it regularly and share your creations in our Discord community and by tagging #Made<wbr />With<wbr />Graphite on social media.
## Upcoming: FOSDEM '25
If you'll be in Brussels, Belgium for the FOSDEM conference in several weeks (February 1–2), be sure to [reach out](/contact) and arrange a plan to meet up, chat, and pick up some Graphite stickers.
## Until next year
Thank you for dedicating the time to read about this latest annual collection of project updates. It has been a privilege leading this community and endeavor since 2021. As we wrap up four years of hard work and venture into the beginnings of a fifth, I am more eager than ever for the adventures that lie in wait during the times ahead.
Happy creating!
@@ -0,0 +1,327 @@
+++
title = "Graphite progress report (Q4 2024)"
date = 2025-03-31
[extra]
banner = "https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024.avif"
banner_png = "https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024.png"
author = "Keavon Chambers & Hypercube"
summary = "Graphite's Q4 2024 update introduces quality of life features across drawing tools and procedural editing."
css = ["/component/demo-artwork.css"]
reddit = "https://www.reddit.com/r/graphite/comments/1jpjqcs/blog_post_graphite_progress_report_q4_2024/"
twitter = "https://x.com/GraphiteEditor/status/1907350199414206604"
bluesky = "https://bsky.app/profile/graphiteeditor.bsky.social/post/3llsxykppjs2c"
+++
[Graphite](/), a new open source 2D procedural graphics editor, has [wrapped up 2024](../year-in-review-2024-highlights-and-a-peek-at-2025) with a fourth quarter (October-December) focused on introducing **quality of life features** for both Graphite's interactive **tools-driven and procedural editing workflows**.
<!-- more -->
All Q4 2024 commits may be [viewed in this list](https://github.com/GraphiteEditor/Graphite/commits/master/?since=2024-10-01&until=2024-12-31) and all noteworthy changes are detailed below.
This is the fourth in our 2024 series of quarterly progress reports. If you missed [the previous post](../graphite-progress-report-q3-2024), be sure to check it out as well. If you'd like to help speed up future progress, please consider [getting involved](/volunteer) with code, QA/bug testing, or art/marketing projects. [Donations](/donate) are also valued, as are [stars on GitHub](https://github.com/GraphiteEditor/Graphite). Follow along and partake in our [Discord community](https://discord.graphite.art), too.
At the time of publication, there is **one week left to apply for a summer 2025 internship with us through Google Summer of Code.** [Learn more here](../internships-for-a-rust-graphics-engine-gsoc-2025) and apply before [April 8 at 18:00 UTC](https://www.wolframalpha.com/input?i=April+8%2C+18%3A00+UTC).
To showcase the newly introduced feature of converting selected nodes into conveniently reusable subgraphs, the new *Parametric Dunescape* artwork is presented below. Every dune layer is generated just by its custom parameters for color, height, and random seed.
<div class="demo-artwork">
<a href="https://editor.graphite.art/#demo/parametric-dunescape">
<img src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Vector art of Parametric Dunescape" />
</a>
<p>
<span>
<em>Parametric Dunescape</em>
</span>
<br />
<span>
<a href="https://editor.graphite.art/#demo/parametric-dunescape">Open this artwork</a> to<br />explore it yourself.
</span>
</p>
</div>
## Additions
<!--
EDITOR'S NOTE: The grammatical structure of each bullet point should follow the form: "Check out our new... [Bullet point description]"
-->
- Node graph support for making a custom node by merging the selected nodes into a subgraph with the node context menu's "Merge Selected Nodes" option, or the shortcut <kbd>Ctrl</kbd><kbd>M</kbd> (macOS: <kbd>⌘</kbd><kbd>M</kbd>) <small>([#2097](https://github.com/GraphiteEditor/Graphite/pull/2097))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/merge-selected-nodes.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/merge-selected-nodes.mp4" type="video/mp4" />
</video>
</div>
- Text controls for line height, character spacing, and wrappable box areas that can be dragged with the Text tool <small>([#2016](https://github.com/GraphiteEditor/Graphite/pull/2016), [#2118](https://github.com/GraphiteEditor/Graphite/pull/2118))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/text-area-controls.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/text-area-controls.mp4" type="video/mp4" />
</video>
</div>
- Pinnable node sections in the Properties panel <small>([commit e6d8c47](https://github.com/GraphiteEditor/Graphite/commit/e6d8c4743d2aff15985c929df2cc7381a61908a0))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/pinnable-node-sections.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/pinnable-node-sections.mp4" type="video/mp4" />
</video>
</div>
- New demo artwork, [*Changing Seasons*](https://editor.graphite.art/#demo/changing-seasons), featured in the [previous progress report](../graphite-progress-report-q3-2024) <small>([commit fa6b5f2](https://github.com/GraphiteEditor/Graphite/commit/fa6b5f298adf395362e1aaa2c07be89fa89eaee2))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/changing-seasons-demo.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/changing-seasons-demo.mp4" type="video/mp4" />
</video>
</div>
- *Offset Path* node that expands or contracts a vector shape <small>([#2030](https://github.com/GraphiteEditor/Graphite/pull/2030))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/offset-path-node.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/offset-path-node.mp4" type="video/mp4" />
</video>
</div>
- *Flatten Vector Elements* node that turns multiple layers of vector paths into a single combined path; and changes to the *Copy to Points*, *Repeat*, and *Circular Repeat* nodes so they output group data instead of a single vector path, allowing each separate layer to be modified by nodes which operate on groups (like *Assign Colors*), or flattened with *Flatten Vector Elements* to have the prior behavior <small>([#2011](https://github.com/GraphiteEditor/Graphite/pull/2011), [#2045](https://github.com/GraphiteEditor/Graphite/pull/2045))</small>
<img src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/flatten-vector-elements-node.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Visual description of the change introduced by the Flatten Vector Elements node and its usage with instancing nodes" />
- Support for *Fill* and *Stroke* nodes with groups, applying to each vector layer within <small>([#2046](https://github.com/GraphiteEditor/Graphite/pull/2046))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/fill-and-stroke-nodes-on-groups.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/fill-and-stroke-nodes-on-groups.mp4" type="video/mp4" />
</video>
</div>
- *Switch* node that routes one of two data connections based on a true or false value <small>([#2064](https://github.com/GraphiteEditor/Graphite/pull/2064))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/switch-node.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/switch-node.mp4" type="video/mp4" />
</video>
</div>
- *Bevel* node that flattens the corners of vector shapes <small>([#2067](https://github.com/GraphiteEditor/Graphite/pull/2067), [#2096](https://github.com/GraphiteEditor/Graphite/pull/2096))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/bevel-node.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/bevel-node.mp4" type="video/mp4" />
</video>
</div>
- *Jitter Points* node that randomly offsets each point in a vector path <small>([commit 7d86bf4](https://github.com/GraphiteEditor/Graphite/commit/7d86bf4abf7edfe6a5d021075e050614bee07c13))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/jitter-points-node.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/jitter-points-node.mp4" type="video/mp4" />
</video>
</div>
- Node insertion button, and layer renaming, directly from the Properties panel <small>([#2072](https://github.com/GraphiteEditor/Graphite/pull/2072), [#2081](https://github.com/GraphiteEditor/Graphite/pull/2081))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/node-insertion-button-in-properties-panel.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/node-insertion-button-in-properties-panel.mp4" type="video/mp4" />
</video>
</div>
- Path tool feature where pressing <kbd>Space</kbd> while dragging a handle makes the anchor be dragged as well <small>([#2065](https://github.com/GraphiteEditor/Graphite/pull/2065))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/path-tool-space-to-drag-anchor.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/path-tool-space-to-drag-anchor.mp4" type="video/mp4" />
</video>
</div>
- Path tool feature where pressing <kbd>Tab</kbd> while dragging a handle makes it swap to the opposite handle <small>([#2058](https://github.com/GraphiteEditor/Graphite/pull/2058))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/path-tool-tab-to-swap-handle.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/path-tool-tab-to-swap-handle.mp4" type="video/mp4" />
</video>
</div>
- Pen tool feature allowing the connection of layers by their endpoints so they both get merged into a single layer <small>([#2076](https://github.com/GraphiteEditor/Graphite/pull/2076))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/pen-tool-combining-layers.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/pen-tool-combining-layers.mp4" type="video/mp4" />
</video>
</div>
- *Clamp* node that limits an input number between a minimum and maximum range <small>([#2087](https://github.com/GraphiteEditor/Graphite/pull/2087))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/clamp-node.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/clamp-node.mp4" type="video/mp4" />
</video>
</div>
- *To U32* and *To U64* nodes that convert numbers to a positive integer type required by a few nodes, as a workaround for automatic type conversion not being fully supported yet <small>([#2087](https://github.com/GraphiteEditor/Graphite/pull/2087))</small>
- *Dot Product* node that calculates the mathematical dot product between two numerical vectors <small>([#2126](https://github.com/GraphiteEditor/Graphite/pull/2126))</small>
- *Math* node that calculates a custom math expression with variables "A" and "B" <small>([#2121](https://github.com/GraphiteEditor/Graphite/pull/2121))</small>
<img src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/math-node.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Visualization of the Math node properties showing the example expression: 0.33 * A + (1 - 0.33) * B" />
- Degrees/radians option in the trig-related math nodes and "Always Positive" option in the *Modulo* node for more convenient usage of the math nodes <small>([commit d649052](https://github.com/GraphiteEditor/Graphite/commit/d649052255c10c15754c3a3707f2edf996d2468d))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/modulo-and-trig-node-additions.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/modulo-and-trig-node-additions.mp4" type="video/mp4" />
</video>
</div>
- Node graph control bar revamp <small>([#2093](https://github.com/GraphiteEditor/Graphite/pull/2093))</small>
<img src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/node-graph-control-bar-revamp.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="View of the control bar area of the UI in the node graph featuring new buttons" />
- Freehand tool feature for drawing new subpaths on an existing vector layer by holding <kbd>Shift</kbd> <small>([commit ed119ad](https://github.com/GraphiteEditor/Graphite/commit/ed119ad3d799030dbc488ccfc8ca9ad057eeff2c))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/freehand-tool-draw-appended-to-layer.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/freehand-tool-draw-appended-to-layer.mp4" type="video/mp4" />
</video>
</div>
- Proper automatic placement of layers into the artboard they're drawn inside of <small>([#2110](https://github.com/GraphiteEditor/Graphite/pull/2110))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/layer-placement-into-artboard-drawn-on.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/layer-placement-into-artboard-drawn-on.mp4" type="video/mp4" />
</video>
</div>
- Menu bar additions of *Layer* > *New*, *Layer* > *Group Selected*, and *Layer* > *Delete Selected* <small>([commit feba874](https://github.com/GraphiteEditor/Graphite/commit/feba87449bb490e47df6f267576bec5ab4238dc3))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/layer-menu-bar-additions.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/layer-menu-bar-additions.mp4" type="video/mp4" />
</video>
</div>
- Select tool box selection feature for subtracting the targetted layers from the active selection with a modifier key as shown in the contextual input hints at the bottom of the editor <small>([#2162](https://github.com/GraphiteEditor/Graphite/pull/2162))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/box-selection-subtraction.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/box-selection-subtraction.mp4" type="video/mp4" />
</video>
</div>
- Path tool feature for snapping to 15° increments and locking the angles of dragged handles when <kbd>Shift</kbd> and <kbd>Ctrl</kbd> modifier keys are pressed <small>([#2160](https://github.com/GraphiteEditor/Graphite/pull/2160))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/path-tool-angle-locking-and-15deg-increments.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/path-tool-angle-locking-and-15deg-increments.mp4" type="video/mp4" />
</video>
</div>
- Support for multiple top output wires extending from the same layer stack <small>([#2049](https://github.com/GraphiteEditor/Graphite/pull/2049))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/layer-stack-multi-top-outputs.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/layer-stack-multi-top-outputs.mp4" type="video/mp4" />
</video>
</div>
- Style improvements to the Layers panel UI to clarify which layers contain selected children, even if hidden within a collapsed layer which previously obscured where selected layers were within the hierarchy <small>([commit 1264ea8](https://github.com/GraphiteEditor/Graphite/commit/1264ea8246cbb06e0602a93be983762ab17adf30))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/layer-panel-show-selected-within-collapsed.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/layer-panel-show-selected-within-collapsed.mp4" type="video/mp4" />
</video>
</div>
- Revamped quick measurement overlays now supporting every layer arrangement scenario <small>([#2147](https://github.com/GraphiteEditor/Graphite/pull/2147), [#2155](https://github.com/GraphiteEditor/Graphite/pull/2155))</small>
<div class="video-background">
<video autoplay loop muted playsinline disablepictureinpicture disableremoteplayback>
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/quick-measurement-improvements.webm" type="video/webm" />
<source src="https://static.graphite.art/content/blog/2025-03-31-graphite-progress-report-q4-2024/quick-measurement-improvements.mp4" type="video/mp4" />
</video>
</div>
## Fixes
- Fix for viewport tools no longer remaining active in the background when the node graph is open <small>([#2093](https://github.com/GraphiteEditor/Graphite/pull/2093))</small>
- Fix to boolean operations so open subpaths are automatically closed <small>([#2014](https://github.com/GraphiteEditor/Graphite/pull/2014))</small>
- Fix for a problem with double clicking an anchor for converting it between smooth and sharp <small>([#2023](https://github.com/GraphiteEditor/Graphite/pull/2023))</small>
- Fix for a *Scatter Points* node breakage <small>([commit 7a56af0](https://github.com/GraphiteEditor/Graphite/commit/7a56af01efc82460e780c78b008a52487972a7eb))</small>
- Fix for properly considering artboard clipping when calculating click targets <small>([#2028](https://github.com/GraphiteEditor/Graphite/pull/2028), [#2036](https://github.com/GraphiteEditor/Graphite/pull/2036))</small>
- Fix for <kbd>Ctrl</kbd><kbd>H</kbd> layer hiding and <kbd>Ctrl</kbd><kbd>L</kbd> layer locking only working with the graph open <small>([#2029](https://github.com/GraphiteEditor/Graphite/pull/2029))</small>
- Fix to artboard label positioning and styling of text overlays <small>([#2032](https://github.com/GraphiteEditor/Graphite/pull/2032))</small>
- Fix for an assortment of crashes and bugs <small>([#2075](https://github.com/GraphiteEditor/Graphite/pull/2075))</small>
- Fix for broken gradient transforms with the Vello renderer <small>([#2059](https://github.com/GraphiteEditor/Graphite/pull/2059))</small>
- Fix for alignment snapping not preserving aspect ratio when <kbd>Shift</kbd> is held <small>([#2062](https://github.com/GraphiteEditor/Graphite/pull/2062))</small>
- Fix for the Text tool clearing the text when hitting <kbd>Esc</kbd> <small>([#2052](https://github.com/GraphiteEditor/Graphite/pull/2052))</small>
- Fix for allowing the Path tool to edit an upstream vector path even if there's a type conversion midway <small>([#2055](https://github.com/GraphiteEditor/Graphite/pull/2055))</small>
- Fix for the number input widget not updating its unit symbol when changing to show another input field <small>([#2080](https://github.com/GraphiteEditor/Graphite/pull/2080))</small>
- Fix to make the *Sample Points*, *Scatter Points*, and *Splines from Points* nodes generate segments (not just invisible points) and work with subpaths <small>([#2085](https://github.com/GraphiteEditor/Graphite/pull/2085))</small>
- Fix for sometimes breaking the selected layer upon switching away from the Select tool <small>([commit 8d3da83](https://github.com/GraphiteEditor/Graphite/commit/8d3da83606c23366d2688602afbc0917e7224e68))</small>
- Fix to remove a visual cutout from the left border of a layer in the node graph when a wire doesn't entering through the cutout <small>([commit 12ca060](https://github.com/GraphiteEditor/Graphite/commit/12ca06035cd7463ed895671ff7eebe53fde655c6))</small>
- Fix to make point nudging with the Path tool work in document space <small>([#2095](https://github.com/GraphiteEditor/Graphite/pull/2095))</small>
- Fix to make the *Spline* node algorithm be continuous across start/end points <small>([#2092](https://github.com/GraphiteEditor/Graphite/pull/2092))</small>
- Fix to properly support layer nudging when the view is tilted and make nudge resizing work in the Artboard tool <small>([#2098](https://github.com/GraphiteEditor/Graphite/pull/2098))</small>
- Fix to disable menu bar entries when no layer is selected <small>([#2098](https://github.com/GraphiteEditor/Graphite/pull/2098))</small>
- Fix for clarifying the present state of the Brush tool with a warning message <small>([commit de366f9](https://github.com/GraphiteEditor/Graphite/commit/de366f951424fcdf4463a419db3fa659910fabfd))</small>
- Fix to load the editor faster by moving font catalog loading to document creation time <small>([commit de366f9](https://github.com/GraphiteEditor/Graphite/commit/de366f951424fcdf4463a419db3fa659910fabfd))</small>
- Fix to make the Pen tool only append new paths when <kbd>Shift</kbd> is held <small>([#2102](https://github.com/GraphiteEditor/Graphite/pull/2102))</small>
- Fix to make the Pen tool always snap to endpoint anchors, even when snapping is off <small>([#2107](https://github.com/GraphiteEditor/Graphite/pull/2107))</small>
- Fix crash when upgrading a document with a *Modulo* node from 3 commits ago <small>([commit 4c4d559](https://github.com/GraphiteEditor/Graphite/commit/4c4d559d97b4d131d2777c0aab19590531ae47a9))</small>
- Fix to clean up the consistency of the editor preferences dialog <small>([commit 99cf8f0](https://github.com/GraphiteEditor/Graphite/commit/99cf8f0c4f91a051b59fc2c9e5cc6c7417bdd74b))</small>
- Fix to remove the inconsistently functioning double-click behavior of switching to the Path tool on vector layers, which previously worked only on layers with a Path node <small>([#2116](https://github.com/GraphiteEditor/Graphite/pull/2116))</small>
- Fix for dragging a pair of colinear handles to break the colinearity so they can move without their anchor <small>([#2120](https://github.com/GraphiteEditor/Graphite/pull/2120))</small>
- Fix for the broken bounding box of image layers, which also impacted their layer thumbnails <small>([#2122](https://github.com/GraphiteEditor/Graphite/pull/2122))</small>
- Fix to restore the keyboard shortcut label in the menu bar's *File* > *Close* menu item <small>([#2135](https://github.com/GraphiteEditor/Graphite/pull/2135))</small>
- Fix to the syntax of exported SVG files that minorly deviated from spec and may have impact some strict SVG viewers <small>([#2131](https://github.com/GraphiteEditor/Graphite/pull/2131))</small>
- Fix for the UI by removing most "coming soon" elements left over from earlier times when placeholders were necessary <small>([commit 1264ea8](https://github.com/GraphiteEditor/Graphite/commit/1264ea8246cbb06e0602a93be983762ab17adf30))</small>
- Fix for issues with selection history <small>([#2138](https://github.com/GraphiteEditor/Graphite/pull/2138))</small>
- Fix for a cancellation of a transform cage rotation causing broken state upon the next transformation <small>([#2149](https://github.com/GraphiteEditor/Graphite/pull/2149))</small>
- Fix to make the Path tool deselect all of a filled shape's points when single-clicked, and select all when double-clicked <small>([#2148](https://github.com/GraphiteEditor/Graphite/pull/2148))</small>
- Fix for the Select tool's box selection not being able to extend a selection with <kbd>Shift</kbd> <small>([#2157](https://github.com/GraphiteEditor/Graphite/pull/2157))</small>
- Fix for several bugs in vector-related nodes <small>([commit b81f483](https://github.com/GraphiteEditor/Graphite/commit/b81f48385afc8c9c27820ffe8d5953529f89b7bd))</small>
- Fix for the Text tool making it easier to select existing text layers with more forgiving click targets <small>([#2145](https://github.com/GraphiteEditor/Graphite/pull/2145))</small>
- Fix for text layers getting deselected after clicking out of Text tool's interactive editing mode <small>([#2144](https://github.com/GraphiteEditor/Graphite/pull/2144))</small>
- Fix to make the Artboard tool shift its contents if resizing from the top/left so artwork remains stationary <small>([#2166](https://github.com/GraphiteEditor/Graphite/pull/2166))</small>
## Internal
- Change to make the *Upload Texture* node resolution-aware <small>([#2018](https://github.com/GraphiteEditor/Graphite/pull/2018))</small>
- Fix for faulty contravariance checking in the type system <small>([#2025](https://github.com/GraphiteEditor/Graphite/pull/2025))</small>
- Update of Wasm dependencies to fix a crash in Firefox with Vello due to a WebGPU spec change <small>([#2027](https://github.com/GraphiteEditor/Graphite/pull/2027))</small>
- Simplification of the Bezier-rs interactive web demo code <small>([#2020](https://github.com/GraphiteEditor/Graphite/pull/2020), [commit 4df7803](https://github.com/GraphiteEditor/Graphite/commit/4df780391c0cbb87b12812d0249ed9b62e2740e4))</small>
- Fix for Clippy code warnings <small>([commit a395fbf](https://github.com/GraphiteEditor/Graphite/commit/a395fbf0637c23b8b05a7c451ff7b8421587b655), [#2119](https://github.com/GraphiteEditor/Graphite/pull/2119))</small>
- Refactor of assorted parts of the RawKit crate <small>([#1972](https://github.com/GraphiteEditor/Graphite/pull/1972), [#2071](https://github.com/GraphiteEditor/Graphite/pull/2071), [#2066](https://github.com/GraphiteEditor/Graphite/pull/2066), [#2082](https://github.com/GraphiteEditor/Graphite/pull/2082), [#2088](https://github.com/GraphiteEditor/Graphite/pull/2088))</small>
- Code tidyness cleanup for node ID generation <small>([#2009](https://github.com/GraphiteEditor/Graphite/pull/2009))</small>
- Refactor collection of snap targets <small>([#2114](https://github.com/GraphiteEditor/Graphite/pull/2114))</small>
- System for parsing node and parameter descriptions from doc comments, enabling better node tooltips going forward <small>([#2089](https://github.com/GraphiteEditor/Graphite/pull/2089), [#2163](https://github.com/GraphiteEditor/Graphite/pull/2163), [commit d649052](https://github.com/GraphiteEditor/Graphite/commit/d649052255c10c15754c3a3707f2edf996d2468d))</small>
- Fix for the rectangle constructor in the Bezier-rs library to produce linear segments <small>([#2109](https://github.com/GraphiteEditor/Graphite/pull/2109))</small>
- Math-Parser library for reading and evaluating math expressions <small>([#2033](https://github.com/GraphiteEditor/Graphite/pull/2033))</small>
- Simplifications to the implementation of buffered message passing within the editor architecture <small>([#2123](https://github.com/GraphiteEditor/Graphite/pull/2123))</small>
- Updates to dependencies <small>([#2134](https://github.com/GraphiteEditor/Graphite/pull/2134))</small>
## Website
- Publication of the previous blog post, [*Graphite progress report (Q3 2024)*](../graphite-progress-report-q3-2024) <small>([#2013](https://github.com/GraphiteEditor/Graphite/pull/2013))</small>
- Updates to the screenshots on the home page carousel <small>([commit f892687](https://github.com/GraphiteEditor/Graphite/commit/f89268757614bda5f949144f84ccc79bd33ddec2))</small>
- Updates to the website roadmap and other details <small>([commit d7a271f](https://github.com/GraphiteEditor/Graphite/commit/d7a271f67595863835e80e58636fead9b1fe0a1d), [commit ce0cd39](https://github.com/GraphiteEditor/Graphite/commit/ce0cd39c9b4291e76d59d5b7c427afd39702aed6), [commit 740fcb7](https://github.com/GraphiteEditor/Graphite/commit/740fcb73cc1202d89107a08f3dfccd88dd17a6a3), [commit 54926d5](https://github.com/GraphiteEditor/Graphite/commit/54926d5474f3bcaffd54d0fd0d2509e989cfa425))</small>
@@ -0,0 +1,46 @@
+++
title = "Internships for a Rust graphics engine: GSoC 2025"
date = 2025-04-02
[extra]
banner = "https://static.graphite.art/content/blog/2025-04-02-internships-for-a-rust-graphics-engine-gsoc-2025.avif"
banner_png = "https://static.graphite.art/content/blog/2025-04-02-internships-for-a-rust-graphics-engine-gsoc-2025.png"
author = "Keavon Chambers"
summary = "Join Graphite in Google Summer of Code 2025 for a unique opportunity to contribute to open-source software development in Rust and computer graphics. Get paid while learning, working on self-contained projects under experienced mentors, and help Graphite grow."
reddit = "https://www.reddit.com/r/graphite/comments/1jplm6t/internships_for_a_rust_graphics_engine_gsoc_2025/"
twitter = "https://x.com/GraphiteEditor/status/1907384498389651663"
bluesky = "https://bsky.app/profile/graphiteeditor.bsky.social/post/3llt7lbmm4s24"
js = ["/js/component/youtube-embed.js"]
css = ["/component/youtube-embed.css"]
+++
Do you love Rust and focus on computer graphics, compiler development, or algorithm research? Students (and non-students who are new to open source contribution) interested in a paid summer internship are invited to apply this week to Graphite's Google Summer of Code 2025 program. The deadline is [April 8 at 18:00 UTC](https://www.wolframalpha.com/input?i=April+8%2C+18%3A00+UTC).
<!-- more -->
[Google Summer of Code](https://summerofcode.withgoogle.com/) is a program that supports students and open source projects. It provides opportunities for summer-long internship experiences with [paid stipends](https://developers.google.com/open-source/gsoc/help/student-stipends) for completing projects with mentoring organizations. [Graphite is honored to be returning for a second year.](https://summerofcode.withgoogle.com/programs/2025/organizations/graphite)
[Graphite](/) is a data-driven creative design engine that combines an artist-friendly image editing environment with a procedural graphics renderer built with Graphene, a custom Rust-based compiled functional programming language for portable, scriptable graphics pipelines.
Hear from our mentoring team about Graphite's tech stack and engineering vision by listening to last month's episode of the Developer Voices podcast:
{{ youtube_embed(id="ZUbcwUC5lxA", title="Graphite: Image Editing as a Syntax Tree (with Keavon Chambers & Dennis Kobert)") }}
For GSoC 2025, our priority focus is mentoring projects revolving around advanced graphics techniques and compiler development, meeting our goal of extending Graphite into the domain of raster image manipulation. Demonstrated experience in these areas is highly advantageous.
Outstanding applicants tend to be competent, self-directed, productive, and confidence-inspiring. If you're driven to learn and explore cutting-edge engineering challenges, please [get in touch](https://discord.graphite.art) and we will help you get up to speed and assist in submitting a timely application.
Out of the [full list of project opportunities](/volunteer/guide/student-projects/#project-idea-list), some interesting high-priority projects include:
- Inventing efficient rendering of connected "mesh vector" Bézier geometry
- Extending the Graphene compiler to run GPU compute shader graphics pipelines
- Implementing bidirectional type inference into the Graphene type system
- Developing a graph theory-based system for equivalence rewriting of nodes
- Creating an efficient brush rendering system for large painted path datasets
- Managing colors through flexible use of color spaces, color models, and color profiles
- Researching an assortment of algorithms for image processing operations
- Engineering solutions for combining our web-based editor GUI and native WGPU renderer in the same window across Windows, Mac, and Linux desktop applications
These recently added projects are pending an extended description, but we'll be happy to discuss the details if you pop into our [Discord server](https://discord.graphite.art) and introduce yourself with links to related experience and projects.
Outside GSoC, additional year-round opportunities are available for student capstone or independent research projects. These are run similarly, but are also available to multi-person groups. They provide academic credit instead of a stipend. Reach out anytime if interested.
@@ -0,0 +1,29 @@
+++
title = "Graphite community meetup in Germany"
date = 2025-09-19
[extra]
banner = "https://static.graphite.art/content/blog/2025-09-19-graphite-community-meetup-in-germany.avif"
banner_png = "https://static.graphite.art/content/blog/2025-09-19-graphite-community-meetup-in-germany.png"
author = "Keavon Chambers"
summary = "Join us for a Graphite community meetup on October 10th, 2025 in Karlsruhe, Germany. Meet the core team and connect with fellow enthusiasts."
reddit = "https://www.reddit.com/r/graphite/comments/1nlt64g/graphite_community_meetup_in_germany_october_10/"
twitter = "https://x.com/GraphiteEditor/status/1969324821205925934"
bluesky = "https://bsky.app/profile/graphiteeditor.bsky.social/post/3lzaz3uizkc2j"
+++
To our European community: mark your calendars for the first official Graphite community meetup on **Friday, October 10th, 2025** in **Karlsruhe, Germany**. Meet core members of the project team and make acquaintance with your fellow Graphite enthusiasts plus Rust developers, open source contributors, and FOSS digital content creation software users.
<!-- more -->
To ensure we are prepared with enough space and food, [**please RSVP as soon as possible**](https://luma.com/d0psfopk) and remember to return to update the registration if your plans change.
We are planning to host a mix of short workshops, talks, open-ended design discussions, socializing, and show-and-tell demos (including a hands-on sneak peek of the upcoming desktop app) based on the interests of attendees. [Get in touch](/contact) if you would like to present a talk or demo of how you've been using Graphite. Activities will begin at 16:00 and run until 22:00, with scheduling broken into both afternoon and evening sessions to accommodate options for earlier availability and later arrivals. The schedule of events will take a freeform "unconference" style in the afternoon followed by somewhat more structured plans in the evening. You are invited to attend all sessions or drop in for the later parts as your schedule and travel plans allow. Feel free to prepare suggestions for topics that we can jump between every half hour.
In attendance will be:
- Keavon Chambers, Graphite founder, designer, and project lead
- Dennis Kobert, technical lead and core maintainer
- Timon Schelling, developer of the Graphite desktop app
- Sebastian Sydow, contributor to Graphite's GPU infrastructure and <a href="https://rust-gpu.github.io/" target="_blank">Rust GPU</a> maintainer
We look forward to seeing you there.
@@ -0,0 +1,28 @@
+++
title = "Blog"
template = "blog.html"
page_template = "article.html"
sort_by = "date"
generate_feeds = true
+++
<section id="intro">
<div class="block">
# Blog
<div class="left-right-split">
Latest news and articles from the Graphite team.
<p class="feed">
<a href="/blog/rss.xml">
<img class="icon" src="https://static.graphite.art/icons/feed.svg" alt="RSS" />
<span class="link arrow">RSS Feed</span>
</a>
</p>
</div>
</div>
</section>
@@ -0,0 +1,23 @@
+++
title = "Contact the team"
[extra]
css = ["/layout/reading-material.css"]
+++
<section class="reading-material">
<div class="block">
# Contact the team
<article>
- Members of the press, please see the [press resources](/press) page.
- For general discussions, reach out on [Discord](https://discord.graphite.art) or [Reddit](https://www.reddit.com/r/graphite/).
- To report a bug or request a feature, please [file an issue](https://github.com/GraphiteEditor/Graphite/issues/new) on GitHub.
- For other inquiries, get in touch by email at [contact<wbr>@graphite<wbr>.art](mailto:contact@graphite.art).
</article>
</div>
</section>
@@ -0,0 +1,222 @@
+++
title = "Donate"
[extra]
css = ["/page/donate.css", "/component/feature-box.css", "/component/feature-icons.css"]
+++
<section>
<div class="block">
# Funding creativity, not corporations
**Own your tools. Own your art.** Invest in a sustainable, independent future for high-quality creative software that cannot ever be taken away.
<div class="call-to-action">
<span>
<a href="https://github.com/sponsors/GraphiteEditor" target="_blank" class="button arrow">Donate: GitHub Sponsors</a>
<em>Avoids processing fees</em>
</span>
<span>
<a href="#supporter-memberships" class="button arrow">Donate: without an account</a>
<em>Start to finish in several seconds</em>
</span>
</div>
<div class="feature-icons three-wide statistics" data-statistics>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 34" src="https://static.graphite.art/icons/icon-atlas-roadmap__3.png" alt="" />
<span data-statistics-dollars></span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 3" src="https://static.graphite.art/icons/icon-atlas-features__2.png" alt="" />
<span data-statistics-members></span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 47" src="https://static.graphite.art/icons/icon-atlas-roadmap__3.png" alt="" />
<span data-statistics-donors></span>
</div>
</div>
<script>
(async () => {
const element = document.querySelector("[data-statistics]");
const dollarsElement = document.querySelector("[data-statistics-dollars]");
const membersElement = document.querySelector("[data-statistics-members]");
const donorsElement = document.querySelector("[data-statistics-donors]");
if (!dollarsElement || !membersElement || !donorsElement) return;
try {
const response = await fetch("https://graphite.art/sponsorship-stats");
const json = await response.json();
if (!json || !json.recurring || !json.one_time_prior_3_month_sum) throw new Error();
const recurringDollars = parseInt(json.recurring.cents) / 100;
const oneTimeAverageDollars = parseInt(json.one_time_prior_3_month_sum.cents) / 100 / 3;
dollarsElement.innerText = "$" + Math.round(recurringDollars + oneTimeAverageDollars).toLocaleString("en-US") + " / month";
membersElement.innerText = json.recurring.count.toLocaleString("en-US") + " members (supporting monthly)";
donorsElement.innerText = Math.round(json.one_time_prior_3_month_sum.count / 3).toLocaleString("en-US") + " one-time donors (past month)";
// Force repaint to work around Safari bug <https://bugs.webkit.org/show_bug.cgi?id=286403> (remove this and its data attribute when the bug is fixed and widely deployed)
element.style.transform = "scale(1)";
} catch {
element.remove();
}
})();
</script>
Graphite is 100% built and funded by the community. Your contributions directly help us level up the scope and speed of the project's development. Resources are put towards infrastructure, operational costs, swag to keep contributors happy and motivated, and outreach like exhibiting at conventions and traveling to conferences to foster industry relationships. Hiring full-time developers is the next crucial milestone.
</div>
</section>
<section id="supporter-memberships" class="feature-box-outer">
<div class="feature-box-inner">
<div class="block">
<h1 class="feature-box-header">Supporter memberships</h1>
---
Click a membership level below to pay directly by card, no account needed.
A small fee of 3.6% + 30¢ reduces what we receive each month. If convenient, consider instead using <a href="https://github.com/sponsors/GraphiteEditor" target="_blank">GitHub Sponsors</a> for **no fees**.
</div>
<div class="triptych">
<a href="https://buy.stripe.com/6oE2btfCK9863vybII" target="_blank" class="block feature-box-narrow">
<h1 class="feature-box-header">⭕ &ldquo;Quark&rdquo; &raquo;</h1>
**$5 / month**
- Your GitHub profile unlocks a shiny achievement acknowledging your contribution *(through GitHub Sponsors only)*
</a>
<a href="https://buy.stripe.com/00gdUb62aesq9TW7st" target="_blank" class="block feature-box-narrow">
<h1 class="feature-box-header">➕ &ldquo;Proton&rdquo; &raquo;</h1>
**$10 / month**
- Get a **"Member" role** and accompanying **gold-colored nametag on Discord**
- *Plus the lower-tier rewards*
</a>
<a href="https://buy.stripe.com/5kAbM38aiacaeac28a" target="_blank" class="block feature-box-narrow">
<h1 class="feature-box-header">⚛️ &ldquo;Carbon&rdquo; &raquo;</h1>
**$15 / month**
- Your name/handle listed in the end-of-year **retrospective blog post** thank-you section
- *Plus the lower-tier rewards*
</a>
<a href="https://buy.stripe.com/28o4jB62a0BA5DGbIL" target="_blank" class="block feature-box-narrow">
<h1 class="feature-box-header">🧬 &ldquo;DNA&rdquo; &raquo;</h1>
**$25 / month**
- Your **personal name** (or handle) **on the Graphite website and GitHub readme**
- Option to be mailed a personal **thank-you card with Graphite stickers** (in the US only)
- *Plus the lower-tier rewards*
</a>
<a href="https://buy.stripe.com/28o03laiq0BA8PS6os" target="_blank" class="block feature-box-narrow">
<h1 class="feature-box-header">🌱 &ldquo;Organism&rdquo; &raquo;</h1>
**$50 / month**
- Option to be given a public **shout-out of appreciation** from @GraphiteEditor on your choice of social media sites
- *Plus the lower-tier rewards*
</a>
<a href="https://buy.stripe.com/fZedUbduCfwu2ru7sx" target="_blank" class="block feature-box-narrow">
<h1 class="feature-box-header">🌄 &ldquo;Biosphere&rdquo; &raquo;</h1>
**$75 / month**
- Your personal name (or handle) may be a **hyperlink** to your personal site or social media profile
- *Plus the lower-tier rewards*
</a>
</div>
<div class="block action-buttons">
<a href="https://donate.stripe.com/6oU8wP6m0c2kb2AermbQY0a" target="_blank" class="button arrow">Or make a one-time donation</a>
[Manage your ongoing membership](https://billing.stripe.com/p/login/aEU9EzctSfe3cfK5kk)
</div>
</div>
</section>
<section id="corporate-sponsorships" class="feature-box-outer">
<div class="feature-box-inner">
<div class="block">
<h1 class="feature-box-header">Corporate sponsorships</h1>
---
Also available to individuals wanting to make a larger impact. [Reach out](/contact) to pay by invoice or ACH to avoid fees, or for a custom arrangement.
</div>
<div class="triptych">
<a href="https://buy.stripe.com/7sI6rJ1LU5VUaY05kq" target="_blank" class="block feature-box-narrow">
<h1 class="feature-box-header">🪨 &ldquo;Charcoal&rdquo; &raquo;</h1>
**$100 / month**
- Your **company name** may be shown **on the Graphite website and GitHub readme** starting at this tier level
- *Plus the lower-tier rewards for members*
</a>
<a href="https://buy.stripe.com/3cs8zR8ai0BA8PSaEL" target="_blank" class="block feature-box-narrow">
<h1 class="feature-box-header">🛡️ &ldquo;Carbide&rdquo; &raquo;</h1>
**$250 / month**
- Your name on the website and readme may be a **hyperlink** to your company/personal site
- *Plus the lower-tier rewards for members*
</a>
<a href="https://buy.stripe.com/fZeaHZ76e0BAeaccMU" target="_blank" class="block feature-box-narrow">
<h1 class="feature-box-header">💎 &ldquo;Diamond&rdquo; &raquo;</h1>
**$500 / month**
- Your name and link on the website and readme may instead be a **hyperlinked logo**
- *Plus the lower-tier rewards for members*
</a>
</div>
<div class="block action-buttons">
<a href="https://donate.stripe.com/6oU8wP6m0c2kb2AermbQY0a" target="_blank" class="button arrow">Or make a one-time donation</a>
[Manage your ongoing membership](https://billing.stripe.com/p/login/aEU9EzctSfe3cfK5kk)
</div>
</div>
</section>
@@ -0,0 +1,461 @@
+++
title = "Graphite features"
[extra]
css = ["/page/features.css", "/component/feature-box.css", "/component/feature-icons.css", "/component/youtube-embed.css"]
js = ["/js/component/youtube-embed.js"]
+++
<section>
<div class="block">
# Graphite features
The current alpha version of Graphite is a tool for vector art and graphic design. It also supports a limited, experimental raster editing toolset. This tooling is built around a procedural graphics engine, letting artists build complex graphics and animations in its visual scripting language.
</div>
</section>
<section>
<div class="block">
<div class="block video-container">
<div>
{{ youtube_embed(id="ZUbcwUC5lxA", title="Rust-Powered Graphics Editor: How Graphite's Syntax Trees Revolutionize Image Editing") }}
</div>
</div>
</div>
</section>
<section>
<div class="diptych">
<div class="block">
## Layers & nodes: hybrid editing
Graphite combines the best ideas from multiple categories of digital content creation software to reimagine the workflows of 2D graphics editing. It is influenced by the core editing experience of traditional layer-based raster and vector tools, the nondestructive approaches of VFX compositing programs used by film studios, and the boundless creative possibilities of procedural production tools daily-driven by the 3D industry.
Classic layer-based image editing is easy to understand, employing collapsable folders that help artists stay organized. A variety of interactive viewport tools make it easy to manipulate the layers by drawing directly onto the canvas. On the other hand, node-based editing is essentially artist-friendly programming. It works by describing manipulations as steps in a flowchart, which is vastly more powerful but comes with added complexity.
The hybrid workflow pioneered by Graphite is able to deliver a classic tool-centric, layer-based editing experience built around a procedural, node-based compositor. Users can ignore the node graph, use it exclusively, or switch back and forth with the press of a button while creating content. Interacting with the canvas using tools will manipulate the nodes behind the scenes. And the layer panel and node graph provide two equivalent, interchangeable views of the same document structure.
</div>
<div class="block">
## Raster & vector: sharp at all sizes
Digital 2D art commonly takes two forms. Raster artwork is made out of pixels which means it can look like anything imaginable, but it becomes blurry or pixelated when upscaling to a higher resolution. Vector artwork is made out of curved shapes which is perfect for some art styles but limiting to others. The magic of vector is that its mathematically-described curves can be enlarged to any size and remain crisp.
Other apps commonly focus on just raster or vector, forcing artists to buy and learn separate products for both. Mixing art styles requires shuttling content back and forth between programs. And since picking a raster document resolution is a one-time commitment, artists often choose to start out big, resulting in sluggish editing performance and multi-gigabyte documents.
Graphite reinvents raster rendering so it stays sharp at any scale. Artwork is treated as data, not pixels, and is always redrawn at the current viewing resolution. Zoom the viewport and export images at any size— the document's paint brushes, masks, filters, and effects will always be rendered in full detail.
Marrying vector and raster under one roof enables both art forms to complement each other in one cohesive creative workflow. *(Scalable raster compositing is still experimental.)*
</div>
</div>
</section>
<section>
<div class="block">
## Roadmap
<div class="roadmap">
<div class="feature-icons">
<!-- Pre-Alpha -->
<div class="feature-icon complete heading" data-year="2021">
<h3>— Pre-Alpha —</h3>
</div>
<div class="feature-icon complete" title="Development Complete">
<img class="atlas" style="--atlas-index: 1" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Editor systems; basic vector art tools</span>
</div>
<!-- Alpha 1 -->
<div class="feature-icon complete heading" data-year="2022">
<h3>— Alpha 1 —</h3>
</div>
<div class="feature-icon complete" title="Development Complete">
<img class="atlas" style="--atlas-index: 2" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Better tools; node graph prototyping</span>
</div>
<!-- Alpha 2 -->
<div class="feature-icon complete heading" data-year="2023">
<h3>— Alpha 2 —</h3>
</div>
<div class="feature-icon complete" title="Development Complete">
<img class="atlas" style="--atlas-index: 6" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Node graph integration in documents</span>
</div>
<!-- Alpha 3 -->
<div class="feature-icon complete heading" data-year="2024">
<h3>— Alpha 3 —</h3>
</div>
<div class="feature-icon complete" title="Development Complete">
<img class="atlas" style="--atlas-index: 8" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Procedural vector editing and usability</span>
</div>
<!-- Alpha 4 -->
<div class="feature-icon ongoing heading" data-year="2025–">
<h3>— Alpha 4 —</h3>
</div>
<div class="feature-icon complete" title="Development Complete">
<img class="atlas" style="--atlas-index: 46" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Parametric animation</span>
</div>
<div class="feature-icon complete" title="Development Complete">
<img class="atlas" style="--atlas-index: 58" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Instancer nodes for looped generation</span>
</div>
<div class="feature-icon complete" title="Development Complete">
<img class="atlas" style="--atlas-index: 59" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Enhanced Pen, Path, and Shape tools</span>
</div>
<div class="feature-icon complete" title="Development Complete">
<img class="atlas" style="--atlas-index: 63" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>List-based graphical data format</span>
</div>
<div class="feature-icon complete" title="Development Complete">
<img class="atlas" style="--atlas-index: 67" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Data panel for graphical introspection</span>
</div>
<div class="feature-icon complete" title="Development Complete">
<img class="atlas" style="--atlas-index: 13" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Layer clipping masks</span>
</div>
<div class="feature-icon complete" title="Development Complete">
<img class="atlas" style="--atlas-index: 12" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>GPU acceleration infrastructure</span>
</div>
<div class="feature-icon complete" title="Development Complete">
<img class="atlas" style="--atlas-index: 66" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>All-around performance optimizations</span>
</div>
<div class="feature-icon complete" title="Development Complete">
<img class="atlas" style="--atlas-index: 73" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Blend tool to morph between shapes</span>
</div>
<div class="feature-icon complete" title="Development Complete">
<img class="atlas" style="--atlas-index: 24" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Dockable panels and tab reordering</span>
</div>
<div class="feature-icon ongoing" title="Development Ongoing">
<img class="atlas" style="--atlas-index: 9" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Custom attributes for list data</span>
</div>
<div class="feature-icon ongoing" title="Development Ongoing">
<img class="atlas" style="--atlas-index: 17" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Stable document format</span>
</div>
<div class="feature-icon ongoing" title="Development Ongoing">
<img class="atlas" style="--atlas-index: 7" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Desktop app <a href="/#desktop-first-and-web-ready">release candidates</a></span>
</div>
<!-- Beta 1 -->
<div class="feature-icon heading" data-year="Early 2026">
<h3>— Beta 1 —</h3>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 7" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Desktop app release and 1.0 launch</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 51" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Context menus throughout the editor</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 40" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Simplified main properties panel</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 64" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Node version management</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 53" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Local fonts access</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 54" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Saving over local files (web version)</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 41" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Timeline panel for animation curves</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 62" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Nested documents as custom nodes</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 56" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Variables and color swatches</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 10" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Expanded imaging model data format</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 28" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Physical measurement units</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 12" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>GPU-accelerated raster rendering</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 65" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Text-on-path tool support</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 3" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Per-glyph text style controls</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 48" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Robust vector mesh editing/rendering</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 50" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Nondestructive shape builder tool</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 71" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Automatic image trace vectorization</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 11" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Broader SVG support including filters</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 45" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Parametric art standalone export</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 5" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>New and improved brush tool</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 43" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Stylus and touch interaction</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 70" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>MIDI and audio-reactive visualization</span>
</div>
<!-- Beta 2 -->
<div class="feature-icon heading" data-year="2027">
<h3>— Beta 2 —</h3>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 52" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Command palette</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 68" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Onion skinning mode for animation</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 69" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Animatable deformation meshes/rigs</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 72" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Simulation domains</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 57" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Signed distance field rendering</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 14" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Procedural PBR material generation</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 16" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Code editor for custom nodes</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 34" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Asset libraries and node marketplace</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 27" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Automation/batch processing tools</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 24" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Multiple adjacent document panels</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 40" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Tear-out panels as separate windows</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 21" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Select mode (marquee masking)</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 0" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Raster adjustments, filters, and effects</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 25" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Liquify and warp transforms</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 19" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Raw photo processing</span>
</div>
<!-- LTS Releases -->
<div class="feature-icon heading" data-year="Future">
<h3>— LTS Releases —</h3>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 31" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Advanced typesetting features</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 32" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>PDF, EPS, AI, DXF, PSD, and TIFF</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 55" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>CMYK, spot color, and ICC profiles</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 33" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>HDR and WCG color handling</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 4" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Outliner panel (node graph tree view)</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 18" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Document history management</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 39" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Offline edit resolution with CRDTs</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 22" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>History brush and clone stamp tools</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 23" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Internationalization and accessibility</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 49" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Neural nodes/tools like Magic Wand</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 20" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Procedural styling of paint brushes</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 60" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Infinite generative vector patterns</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 29" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Geometric constraint system solver</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 30" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Responsive design layout solver</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 61" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Authoring animated SVGs, Lottie, etc.</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 42" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Live video stream compositing</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 44" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Tablet app and keyboard-free controls</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 26" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Media collection manager/browser</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 37" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Cloud document storage/device sync</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 38" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Multiplayer collaborative editing</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 35" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Predictive graph rendering/caching</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 36" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Multi-device distributed rendering</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 15" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span>Hosted rendering accelerator service</span>
</div>
<div class="feature-icon">
<img class="atlas" style="--atlas-index: 47" src="https://static.graphite.art/icons/icon-atlas-roadmap__5.png" alt="" />
<span><em>…and that's all just the beginning…</em></span>
</div>
</div>
</div>
</div>
</section>
<section>
<div class="block">
## Roadmap spotlight: keyframe animation
Coming early 2026, Graphite will expand its animation toolset beyond parametrically-driven motion to include traditional keyframe animation. The Timeline panel pictured below will let animators drive parameters using keyframes and curves through a traditional dopesheet interface.
Node parameters can be set to a constant value in the Properties panel, exposed to the graph for procedural animation, or exposed to a channel in the upcoming Timeline panel for hand-authored keyframing.
The panel will enable users to scrub through time with the playhead and choose between timing with discrete frames or continuous seconds. A dedicated curves editing mode (not pictured) will enable fine-tuning parameters with a labeled Y-axis, while the dopesheet allows individual channels to be expanded to view and edit the shape and smoothness of curves inline.
Work-in-progress design mockup:
<img src="https://static.graphite.art/content/features/mockup-timeline-panel.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="" />
</div>
</section>
<section>
<div class="block">
## Roadmap spotlight: raster image editing
The vision since Graphite's inception has been to open up the traditional raster image editing workflow to the greater degree of flexibility found in node-based compositors, without one approach compromising the ergonomics of the other.
As with Graphite's current vector toolset, raster editing will interpret the user's interactive edits as modifications to the construction of layers in the underlying node graph rather than destructive alterations to layer pixel data (as in other image editors). By containing only a description of the user's editing operations without the data, documents will remain ultra tiny when source assets are linked externally.
Brushes, selection tools, masks, filters, effects, adjustment layers, and other tools used to manipulate raster layers will all be presented in a familiar form when Graphite's raster toolset nears maturity by the end of beta.
Work-in-progress design mockup:
<img src="https://static.graphite.art/content/features/mockup-viewport.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="" />
<img src="https://static.graphite.art/content/features/mockup-node-graph.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="" />
</div>
</section>
@@ -0,0 +1,28 @@
+++
title = "User manual"
template = "book.html"
page_template = "book.html"
[extra]
book = true
js = ["/js/component/youtube-embed.js"]
css = ["/component/youtube-embed.css"]
+++
Welcome to the Graphite user manual. Keep reading to learn how the software can help bring your 2D creative ideas to life.
## More chapters on the way
Additional manual chapters are being added over time. Check back every so often.
## Need help?
If you're ever stuck or confused, ask your questions in the `#🧭user-help` channel of the [Graphite Discord server](https://discord.graphite.art) or post a thread in the [discussion board](https://github.com/GraphiteEditor/Graphite/discussions) on GitHub.
## Jump right in
<!-- If you're eager to skip the reading, head straight to the [hands-on quickstart video](./introduction) in the next chapter for a beginner project walkthrough you can follow along with. -->
The fastest way to get started is to watch and follow along steps-by-step in the hands-on quickstart tutorial:
{{ youtube_embed(id="7gjUhl_3X10", title="Vector Art Quickstart - Graphite, the Open Source 2D Graphics Suite") }}
@@ -0,0 +1,80 @@
+++
title = "Outline Draft"
template = "book.html"
page_template = "book.html"
draft = true
[extra]
order = 0
+++
- # Interface
- # Layers panel
- About layers and folders
- Control bar
- Blend modes and opacity
- Adding folders
- Deleting the selected layers
- The layer list
- Editing layers
- Selection and multi-selection
- Rearranging the hierarchy
- # Properties panel
- Node properties
- Parameters
- Expose button
- Name
- Value inputs
- # Viewport
- # Navigation
- Pan
- Tilt
- Zoom
- # Artboards
- Infinite canvas
- New document artboard
- Adjusting existing artboards
- # Graph
- # INDEX:
- Opening the graph
- Document graph vs. layer graph, limitations
- Adding nodes
- Connecting nodes
## Overlaid node graph editing
Opening the overlaid node graph shows the structure of nodes and layers that compose the document artwork. It's a more detailed view of what the [Layers](../layers-panel) and [Properties](../properties-panel) panels show.
**Nodes** are the entities with left-to-right input **connectors**.
**Layers** are the larger entities shown with thumbnails and a bottom-to-top direction of data stacking. Their purpose is to composite sources of graphical data on top of one another in a **layer stack**. Layers take input from other nodes or layers via a connector on their left side. When that connector is fed by another layer stack, the Layers panel considers it a **group** because it combines one stack into another parent stack.
Layers and nodes are wired together using **links** which send data between the outputs of nodes to the inputs of others. You can wire up a node by dragging from the output connector of one node to the input connector of its destination node. But note that forming cyclic graphs, where a loop can be traced along the links of a set of nodes, is not permitted. Graphical data flows into the **Output** node which then becomes rendered to the document viewport.
### Node/layer controls
When a layer or node is selected, these buttons will show up on the left side of the control bar:
<p><img src="https://static.graphite.art/content/learn/interface/document-panel/node-controls-buttons.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" onload="this.width = this.naturalWidth / 2" alt="The node/layer controls" /></p>
| | |
|-|-|
| Make<span>&nbsp;</span>Hidden/<br />Make<span>&nbsp;</span>Visible | <p>Toggles the visibility state of the layer or node. This is equivalent to the eye icon button displayed beside each layer. If a node or layer is hidden, it gets bypassed in the data flow. <kbd>Ctrl</kbd><kbd>H</kbd> (macOS: <kbd>⌘</kbd><kbd>H</kbd>) is a shortcut for this toggle that can be used from the graph or viewport.</p> |
| Preview/<br />End<span>&nbsp;</span>Preview | <p>Temporarily moves the graph output away from the Output node and the graph output is instead provided by the previewed node. While previewing, the node is styled with a dashed, brighter border. Ending the preview returns responsibility back to the Output node. This is a handy feature for viewing part of a graph without needing to disconnect the actual Output node and manually restore it later. Clicking a node or layer in the graph while holding <kbd>Alt</kbd> is a shortcut for toggling its preview.</p> |
- # Nodes
- # Layers
- # Vector Editing
- # Nodes
- # Layers
- # Raster Editing
- # INDEX:
- Opening the graph
- Document graph vs. layer graph, limitations
- Adding nodes
- Connecting nodes
- # Nodes
- # Layers
- # Tools
- # General tools
- # Vector tools
- # Raster tools
@@ -0,0 +1,80 @@
+++
title = "Interface"
template = "book.html"
page_template = "book.html"
[extra]
order = 2
+++
This chapter formally introduces the concepts and terminology for the user interface (UI) of the Graphite editor. You may skip to the next chapter if you're familiar with the general layout and terms used in industry-standard graphics editors.
## Title bar
The bar running across the top of the editor is called the **title bar**. In the (forthcoming) desktop release of Graphite, this acts as the draggable window frame.
<!-- <p><img src="https://static.graphite.art/content/learn/interface/title-bar.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="The title bar" /></p> -->
### Menu bar
On the left, the [**menu bar**](./menu-bar) provides quick access to many editor, document, and artwork related controls. Its functions are covered in detail on the next page.
<!-- <p><img src="https://static.graphite.art/content/learn/interface/menu-bar.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="The menu bar" /></p> -->
<!-- In the (forthcoming) macOS desktop release, the menu bar is absent from the editor window; its functions are instead located in macOS menu bar. -->
### Window buttons
On the right (and on Mac, the left), the **window buttons** provide platform-specific controls for the application.
<!-- In the (forthcoming) macOS desktop release, this appears on the left side instead. -->
| | |
|-|-|
| **Web** | <p>Fullscreen:</p><p><img src="https://static.graphite.art/content/learn/interface/window-buttons-web__2.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Fullscreen button" /></p> |
| **Windows** | <p>Minimize, maximize/restore down, close:</p><p><img src="https://static.graphite.art/content/learn/interface/window-buttons-windows.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Minimize/maximize/close window buttons" /></p> |
| **Linux** | <p>Minimize, maximize/unmaximize, close:</p><p><img src="https://static.graphite.art/content/learn/interface/window-buttons-linux.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Minimize/maximize/close window buttons" /></p> |
| **macOS** | <p>Close, minimize, fullscreen:</p><p><img src="https://static.graphite.art/content/learn/interface/window-buttons-macos.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Close/minimize/fullscreen window buttons" /></p> |
## Workspace
The **workspace** is the editor's main content area, filled with **panels** arranged next to one another. The **gutter** lines, located between neighboring panels, may be dragged to resize them.
<p><img src="https://static.graphite.art/content/learn/interface/workspace__3.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="The workspace" /></p>
### Panels
Panels are regions of the UI dedicated to a specific purpose. [**Document**](./document-panel), Properties, and Layers are presently the three panel types.
Each panel name is shown in its **panel header**. Panel tabs offer a quick way to swap between multiple panels occupying the same area (currently only documents support this).
Down the road, these tabs will be dockable so the default layout may be customized.
Beneath the panel header, the **panel content** displays the content for its panel type. Each will be described in the following pages.
## Status bar
The bar running across the bottom of the editor is called the **status bar**.
<p><img src="https://static.graphite.art/content/learn/interface/status-bar__2.avif" onload="this.width = this.naturalWidth / 2" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Status bar" /></p>
### Input hints
The **input hints** are presently the only occupant of the status bar. They indicate what common keyboard and mouse inputs are valid in the current context. Hints change with each active tool as well as with the current interaction state. Keep a frequent eye on the hints to discover more features as you work.
Hints with a **`+`** mean that adding the indicated modifier key will change the base action. For example: in the following action, dragging with left-click held down will zoom the canvas; then additionally holding the <kbd>Ctrl</kbd> key will make the zoom action snap to whole increments.
<p><img src="https://static.graphite.art/content/learn/interface/input-hints-plus.avif" onload="this.width = this.naturalWidth / 2" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Example hint" /></p>
Hints with a **`/`** mean that either indicated input combination can be used to trigger the same action. For example: in the following action, either holding the space bar while dragging with the left mouse button held down, or just dragging with the middle mouse button held down, will both pan around the document in the viewport.
<p><img src="https://static.graphite.art/content/learn/interface/input-hints-slash.avif" onload="this.width = this.naturalWidth / 2" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Example hint" /></p>
The following chart describes each icon representing the mouse inputs you can perform so a hint's prescribed action occurs.
| | Clicks | Drags | Others |
|-|:-:|:-:|:-:|
| **Left<br />mouse<br />button** | <img src="https://static.graphite.art/content/learn/interface/mouse-icon-left-click.avif" onload="this.width = this.naturalWidth / 2" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Left click icon" /><br /><br />Left click | <img src="https://static.graphite.art/content/learn/interface/mouse-icon-left-click-drag.avif" onload="this.width = this.naturalWidth / 2" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Left click drag icon" /><br /><br />Left click drag | <img src="https://static.graphite.art/content/learn/interface/mouse-icon-left-double-click.avif" onload="this.width = this.naturalWidth / 2" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Left double-click icon" /><br /><br />Left double-click |
| **Right<br />mouse<br />button** | <img src="https://static.graphite.art/content/learn/interface/mouse-icon-right-click.avif" onload="this.width = this.naturalWidth / 2" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Right click icon" /><br /><br />Right click | <img src="https://static.graphite.art/content/learn/interface/mouse-icon-right-click-drag.avif" onload="this.width = this.naturalWidth / 2" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Right click drag icon" /><br /><br />Right click drag | <img src="https://static.graphite.art/content/learn/interface/mouse-icon-right-double-click.avif" onload="this.width = this.naturalWidth / 2" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Right double-click icon" /><br /><br />Right double-click |
| **Middle<br />mouse<br />button** | <img src="https://static.graphite.art/content/learn/interface/mouse-icon-middle-click.avif" onload="this.width = this.naturalWidth / 2" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Middle click icon" /><br /><br />Middle click | <img src="https://static.graphite.art/content/learn/interface/mouse-icon-middle-click-drag.avif" onload="this.width = this.naturalWidth / 2" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Middle click drag icon" /><br /><br />Middle click drag | <img src="https://static.graphite.art/content/learn/interface/mouse-icon-scroll.avif" onload="this.width = this.naturalWidth / 2" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Scroll up/down icons" /><br /><br />Scroll up/down |
| **No<br />mouse<br />button** | | <img src="https://static.graphite.art/content/learn/interface/mouse-icon-drag.avif" onload="this.width = this.naturalWidth / 2" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Drag icon" /><br /><br />Drag | |
@@ -0,0 +1,108 @@
+++
title = "Document panel"
[extra]
order = 2
+++
The **Document panel** is the main content area where the artwork is displayed and edited using **tools** within the **viewport**. It's also where the **node graph** can be overlaid by pressing <kbd>Ctrl</kbd><kbd>Space</kbd>.
The viewport is for interactive, visual editing of the **canvas**. The node graph is where you can inspect the underlying structure of the document and edit it in a more technical manner when the need arises.
There is one instance of the Document panel per open document file. Each has its own tab labeled with its file name. When a document has unsaved changes, an `*` is included at the end of the name.
The Document panel is composed of three main areas:
- The **control bar** runs across the top of the panel and shows tool and viewport controls.
- The **tool shelf** is the vertical bar that runs down the left of the panel showing the editing tools and working colors.
- The **viewport** fills most of the panel and contains a view of the canvas which can be interactively edited using the tools.
## Control bar
Here is where you control your interaction with the document via active tool and view options.
<!--
### Editing modes
Only the default mode is currently implemented. Others will be added in the future and this dropdown is a placeholder for that.
| | |
|-|-|
| <img src="https://static.graphite.art/content/learn/interface/document-panel/editing-modes__2.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" onload="this.width = this.naturalWidth / 2" alt="The editing modes dropdown menu" /> | The default, **Design Mode**, is for directly editing the artwork.<br /><br />Once implemented, **Select Mode** will be where marquee selections are made to constrain the active tool's edits to a masked area of choice.<br /><br />Once implemented, **Guide Mode** will be for creating guides and constraint systems used for alignment and constraint-based layout. |
-->
### Tool controls
The left side of the control bar has controls pertaining to the active tool. It's empty for some tools.
<p><img src="https://static.graphite.art/content/learn/interface/document-panel/tool-options__2.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Example of the tool controls for the Select tool" /></p>
The example above shows the Select tool controls. Because the Select tool deals with selecting and transforming layers, its controls include:
- The selection mode to determine if the deepest or shallowest layer in a nested hierarchy is selected when clicked.
- The point about which the **transform cage** is centered.
- Actions to align and distribute the selected layers amongst themselves.
- Actions to flip the selected layers horizontally or vertically.
- Actions to cut or combine selected layers with themselves using boolean operations.
Depending on which tool is active, these will change to reflect the pertinent options.
### Viewport controls
The right side of the control bar has controls related to the active document and viewport.
<p><img src="https://static.graphite.art/content/learn/interface/document-panel/viewport-options__2.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="The viewport controls" /></p>
| | |
|-|-|
| **Overlays** | <p>When checked (default), overlays are shown. When unchecked, they are hidden. Overlays are the temporary contextual visualizations (like bounding boxes and vector manipulators) that are usually blue and appear atop the viewport when using tools.</p> |
| **Snapping** | <p>When checked (default), drawing and dragging shapes and vector points means they will snap to other areas of geometric interest like corners or anchor points. When unchecked, the selection moves freely.<br /><br />Fine-grained options are available by clicking the overflow button to access its options popover menu. Each option has a tooltip explaining what it does by hovering the cursor over it.</p><p><img src="https://static.graphite.art/content/learn/interface/document-panel/snapping-popover__5.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" onload="this.width = this.naturalWidth / 2" alt="Snapping options popover menu" /></p><p>Snapping options relating to **Bounding Boxes**:</p><p><ul><li>**Align with Edges**: Snaps to horizontal/vertical alignment with the edges of any layer's bounding box.</li><li>**Corner Points**: Snaps to the four corners of any layer's bounding box.</li><li>**Center Points**: Snaps to the center point of any layer's bounding box.</li><li>**Edge Midpoints**: Snaps to any of the four points at the middle of the edges of any layer's bounding box.</li><li>**Distribute Evenly**: Snaps to a consistent distance offset established by the bounding boxes of nearby layers (due to a bug, **Corner Points** and **Center Points** must be enabled).</li></ul></p><p>Snapping options relating to **Paths**:</p><p><ul><li>**Align with Anchor Points**: Snaps to horizontal/vertical alignment with the anchor points of any vector path.</li><li>**Anchor Points**: Snaps to the anchor point of any vector path.</li><li>**Line Midpoints**: Snaps to the point at the middle of any straight line segment of a vector path.</li><li>**Path Intersection Points**: Snaps to any points where vector paths intersect.</li><li>**Along Paths**: Snaps along the length of any vector path.</li><li>**Normal to Paths**: Snaps a line to a point perpendicular to a vector path (due to a bug, **Intersections of Paths** must be enabled).</li><li>**Tangent to Paths**: Snaps a line to a point tangent to a vector path (due to a bug, **Intersections of Paths** must be enabled).</li></ul></p> |
| **Grid** | <p>When checked (off by default), grid lines are shown and snapping to them becomes active. The initial grid scale is 1 document unit, helping you draw pixel-perfect artwork.</p><ul><li><p>**Type** sets whether the grid pattern is made of squares or triangles.</p><p>**Rectangular** is a pattern of horizontal and vertical lines:</p><p><img src="https://static.graphite.art/content/learn/interface/document-panel/grid-rectangular-popover__2.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" onload="this.width = this.naturalWidth / 2" alt="Snapping options popover menu" /></p><p>It has one option unique to this mode:</p><ul><li>**Spacing** is the width and height of the rectangle grid cells.</li></ul><p>**Isometric** is a pattern of triangles:</p><p><img src="https://static.graphite.art/content/learn/interface/document-panel/grid-isometric-popover__2.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" onload="this.width = this.naturalWidth / 2" alt="Snapping options popover menu" /></p><p>It has two options unique to this mode:</p><ul><li>**Y Spacing** is the height between vertical repetitions of the grid.</li><li>**Angles** is the slant of the upward and downward sloped grid lines.</li></ul></li><li>**Display** gives control over the appearance of the grid. The **Display as dotted grid** checkbox (off by default) replaces the solid lines with dots at their intersection points.</li><li>**Origin** is the position in the canvas where the repeating grid pattern begins from. If you need an offset for the grid where an intersection occurs at a specific location, set those coordinates.</li></ul> |
| **Render Mode** | <p>**Normal** (default): The artwork is rendered normally.</p><p>**Outline**: The artwork is rendered as a wireframe.</p><p>**Pixel Preview**: **Not implemented yet.** The artwork is rendered as it would appear when exported as a bitmap image at 100% scale regardless of the viewport zoom level.</p><p>**SVG Preview**: **Not implemented yet.** The artwork is rendered as it would appear when exported as an SVG image.</p> |
| **Zoom In** | <p>Zooms the viewport in to the next whole increment.</p> |
| **Zoom Out** | <p>Zooms the viewport out to the next whole increment.</p> |
| **Reset Tilt and Zoom to 100%** | <p>Resets the viewport tilt to 0°. Resets the viewport zoom to 100% which matches the canvas and viewport pixel scale 1:1.</p> |
| **Viewport Zoom** | <p>Indicates the current zoom level of the viewport and allows precise values to be chosen.</p> |
| **Viewport Tilt** | <p>Hidden except when the viewport is tilted (use the *View* > *Tilt* menu action). Indicates the current tilt angle of the viewport and allows precise values to be chosen.</p> |
| **Node Graph** | <p>Toggles the visibility of the overlaid node graph.</p> |
## Tool shelf
This narrow bar runs vertically down the left side of the Document panel beside the viewport.
### Tools
<img src="https://static.graphite.art/content/learn/interface/document-panel/tool-shelf__2.avif" style="float: left; width: 64px; padding-right: 40px" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" onload="this.width = this.naturalWidth / 2" alt="The tool shelf" />
Located at the top of the tool shelf area, the **tool shelf** provides a selection of **tools** for interactively editing the artwork. It is composed of three sections:
<div style="margin-left: calc(64px + 40px)">
| | |
|-|-|
| **General tools** | (Gray icons) Used for assorted editing tasks within the viewport. |
| **Vector tools** | (Blue icons) Used for drawing and editing vector shapes, paths, and text. |
| **Raster tools** | (Orange icons) Used for drawing and editing raster image content. The grayed out icons are placeholders for upcoming tools. |
</div>
<div style="clear: both;"></div>
### Working colors
<img src="https://static.graphite.art/content/learn/interface/document-panel/working-colors.avif" style="float: left; width: 64px; padding-right: 40px;" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="The working colors" />
The **working colors** are the two colors used by the active tool. The upper circle is the **primary color**. The lower circle is the **secondary color**.
There are two buttons located underneath: **Swap** which reverses the current color choices, and **Reset** which restores the primary color to black and the secondary color to white.
The tool controls (above the viewport) for some of the tools offer choices for using the primary and secondary colors. For example, the vector drawing tools have **Fill** and **Stroke** options that use the current secondary and primary colors, respectively, as defaults:
<p><img src="https://static.graphite.art/content/learn/interface/document-panel/tool-options-fill-stroke-colors__2.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" onload="this.width = this.naturalWidth / 2" alt="The Fill and Stroke controls for a vector tool's controls" /></p>
These options each allow choices of being driven by the primary working color, secondary working color, or a custom color set just for that tool.
## Viewport
The **viewport** is the view into the canvas. It lets you interact visually with the artwork content. It also displays temporary contextual **overlays** atop the content to assist with your editing.
The viewport is also surrounded by **scrollbars** and **rulers** around its edges. Scrollbars, located along the bottom/right edges, allow scrolling the artwork to show different parts in the viewport. Rulers, located along the top/left edges, offer dimensional information for the area of the canvas that's within view. They can be hidden with the *View* > *Rulers* toggleable menu option.
@@ -0,0 +1,137 @@
+++
title = "Menu bar"
[extra]
order = 1
+++
The **menu bar** is the series of menus running across the top left of the editor's [**title bar**](../#title-bar). It provides organized access to many actions which are described on this page.
Clicking **File**, **Edit**, **Layer**, **Select**, **View**, **Window**, and **Help** opens a dropdown menu with clickable actions. Pay attention to the keyboard shortcut listed on the right of each row in the dropdown menus. Learning to use them can help speed up your workflow.
## Menu actions reference
The rest of this page is intended as a reference resource. Skip ahead to the next page if this is your first read-through of the manual.
### App button
The **app button** appears as the Graphite [logo](/logo). Clicking it opens the website [home page](/).
### File
The **File menu** lists actions related to file handling:
| | |
|-|-|
| **New…** | <p>Opens the **New Document** dialog for creating a blank canvas in a new editor tab.</p><p><img src="https://static.graphite.art/content/learn/interface/menu-bar/dialog-new-document.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="The 'New Document' dialog" /></p><p><ul><li><strong>Name</strong> determines the initial filename of the new document.</li><li><strong>Infinite Canvas</strong>, if set, skips adding an artboard and thereby starts with a boundless white canvas extending in all directions.</li><li><strong>Dimensions</strong> sets the width and height, in pixels, of the initial artboard. Ignored if *Infinite Canvas* is ticked.</li></ul></p> |
| **Open…** | <p>Opens the operating system file picker dialog for selecting a `.graphite` file from disk to be opened in a new editor tab.</p> |
| **Open Demo Artwork…** | <p>Opens the **Demo Artwork** dialog for loading a choice of premade sample artwork files provided for you to explore. Click the button below each image to open it.</p><p><img src="https://static.graphite.art/content/learn/interface/menu-bar/dialog-demo-artwork__3.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="The 'Demo Artwork' dialog" /></p> |
| **Close** | <p>Closes the active document. If it has unsaved changes (denoted by the `*` after the file name), you will be asked to save or discard the changes.</p> |
| **Close All** | <p>Closes all open documents. To avoid accidentally losing unsaved work, you will be asked to confirm that you want to proceed which will discard the unsaved changes in all open documents.</p> |
| **Save** | <p>Saves the active document by writing the `.graphite` file to disk. An operating system file download dialog may appear asking where to place it. That dialog will provide an opportunity to save over a previous version of the file, if you wish, by picking the identical name instead of saving another instance with a number after it.</p> |
| **Import…** | <p>Opens the operating system file picker dialog for selecting an image file from disk to be placed as a new bitmap image layer or SVG content into the active document.</p> |
| **Export…** | <p>Opens the **Export** dialog for saving the artwork as a *File Type* of *PNG*, *JPG*, or *SVG*. *Scale Factor* multiplies the content's document scale, so a value of 2 would export 300x400 content as 600x800 pixels. *Bounds* picks what area to render: *All Artwork* uses the bounding box of all layers, *Selection* uses the bounding box of the currently selected layers, and an *Artboard: \[Name\]* uses the bounds of that artboard.<br /><br /><img src="https://static.graphite.art/content/learn/interface/menu-bar/dialog-export.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="The 'Export' dialog" /></p> |
| **Preferences…** | <p>Opens the **Editor Preferences** dialog for configuring Graphite's settings.<br /><br /><img src="https://static.graphite.art/content/learn/interface/menu-bar/dialog-editor-preferences__2.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="The 'Editor Preferences' dialog" /></p> |
### Edit
The **Edit menu** lists actions related to the editing workflow:
| | |
|-|-|
| **Undo** | <p>Steps back in the history of changes in the active document.</p> |
| **Redo** | <p>Steps forward in the history of changes in the active document.</p> |
| **Cut** | <p>Copies the selected layer(s) to the clipboard, then deletes them.</p> |
| **Copy** | <p>Copies the selected layer(s) to the clipboard.</p> |
| **Paste** | <p>Pastes the copied layer(s) from the clipboard into the document. It will end up directly above the selected layer, or otherwise at the base of the folder structure.</p><p>In the web version of Graphite, your browser will ask for permission to read from your clipboard which you must grant; alternatively, using the hotkey <kbd>Ctrl</kbd><kbd>V</kbd> (macOS: <kbd>⌘</kbd><kbd>V</kbd>) works without the browser needing this permission.</p> |
| **Duplicate** | <p>Creates a copy of the selected layer(s) directly above their original(s) in the layer stack.</p> |
| **Delete** | <p>Removes all selected layers and folders.</p> |
| **Convert to Infinite Canvas** | <p>Replaces all artboards in the document with standard layers. With no artboards present, the document becomes an infinite canvas.</p> |
### Layer
The **Layer menu** lists actions related to the layers within a document:
| | |
|-|-|
| **New** | <p>Creates a new layer in the active document. It will end up directly above the selected layer, or otherwise at the base of the folder structure.</p> |
| **Group** | <p>Creates a new folder in place of the selected layer(s), then moves them into that folder.</p> |
| **Ungroup** | <p>Removes the selected folder(s), moving their contents up one level in the layer stack.</p> |
| **Hide/Show** | <p>Toggles visibility of the selected layer(s), including or excluding them from rendering as part of the artwork.</p> |
| **Lock/Unlock** | <p>Toggles the locked state of the selected layer(s), preventing them from being selected by tools in the viewport.</p> |
| **Grab** | <p>Begin grabbing the selected layer(s) to translate (move) them around with your cursor's movement. Lock to an axis with <kbd>X</kbd> or <kbd>Y</kbd> then use the number keys to type a pixel distance value. Confirm with a left click or <kbd>Enter</kbd>. Cancel with a right click or <kbd>Esc</kbd>.</p> |
| **Rotate** | <p>Begin rotating the selected layer(s) around their pivot point with your cursor's movement. Use the number keys to type an angle value in degrees. Confirm with a left click or <kbd>Enter</kbd>. Cancel with a right click or <kbd>Esc</kbd>.</p> |
| **Scale** | <p>Begin scaling the selected layer(s) around their pivot point with your cursor's movement. Lock to an axis with <kbd>X</kbd> or <kbd>Y</kbd>. Use the number keys to type a scale multiplier value. Confirm with a left click or <kbd>Enter</kbd>. Cancel with a right click or <kbd>Esc</kbd>.</p> |
| **Arrange ><br />Raise to Front** | <p>Reorders the selected layer(s) above all others within their same folder(s), so they appear in the layer stack and render above those other layers.</p> |
| **Arrange ><br />Raise** | <p>Reorders the selected layers(s) up by one in the layer stack, so any layer that was immediately above the selected layer(s) ends up immediately below.</p> |
| **Arrange ><br />Lower** | <p>Reorders the selected layers(s) down by one in the layer stack, so any layer that was immediately below the selected layer(s) ends up immediately above.</p> |
| **Arrange ><br />Lower to Back** | <p>Reorders the selected layer(s) below all others within their same folder(s), so they appear in the layer stack and render below those other layers.</p> |
| **Arrange ><br />Reverse** | <p>Reorders the selected layers by swapping their positions from top to bottom. Reversal applies amongst each set of selected sibling layers (those with a shared parent).</p> |
| **Align ><br />Align Left** | <p>Moves the selected layer(s) so their left edges line up with the leftmost edge of the selection's bounding box.</p> |
| **Align ><br />Align Horizontal Center** | <p>Moves the selected layer(s) so their horizontal centers line up with the horizontal center of the selection's bounding box.</p> |
| **Align ><br />Align Right** | <p>Moves the selected layer(s) so their right edges line up with the rightmost edge of the selection's bounding box.</p> |
| **Align ><br />Align Top** | <p>Moves the selected layer(s) so their top edges line up with the topmost edge of the selection's bounding box.</p> |
| **Align ><br />Align Vertical Center** | <p>Moves the selected layer(s) so their vertical centers line up with the vertical center of the selection's bounding box.</p> |
| **Align ><br />Align Bottom** | <p>Moves the selected layer(s) so their bottom edges line up with the bottommost edge of the selection's bounding box.</p> |
| **Flip ><br />Flip Horizontal** | <p>Reflects the selected layer(s) horizontally within the selection's bounding box.</p> |
| **Flip ><br />Flip Vertical** | <p>Reflects the selected layer(s) vertically within the selection's bounding box.</p> |
| **Turn ><br />Turn -90°** | <p>Rotates the selected layer(s) a quarter turn counterclockwise about the selection's bounding box center.</p> |
| **Turn ><br />Turn 90°** | <p>Rotates the selected layer(s) a quarter turn clockwise about the selection's bounding box center.</p> |
| **Boolean ><br />Union** | <p>Combines all paths of the selected vector layer(s) while cutting out overlapping areas (even the interiors of a single path)
| **Boolean ><br />Subtract Front** | <p>Cuts overlapping areas out from the last of the selected vector layers.</p> |
| **Boolean ><br />Subtract Back** | <p>Cuts overlapping areas out from the first of the selected vector layers.</p> |
| **Boolean ><br />Intersect** | <p>Cuts away all but the overlapping areas shared by every path of the selected vector layer(s).</p> |
| **Boolean ><br />Difference** | <p>Cuts away the overlapping areas shared by every path of the selected vector layer(s), leaving only the non-overlapping areas.</p> |
| **Make Path Editable** | <p>Applies a path edit operation (the **Path node**) to the selected vector layer, capturing the geometry after other nondestructive operations to enable its direct modification by the **Path** and **Pen** tools.</p> |
### Select
The **Select menu** lists actions related to the selection of layers within a document:
| | |
|-|-|
| **Select All** | <p>Selects all layers and folders in the document.</p> |
| **Deselect All** | <p>Deselects everything in the document.</p> |
| **Select Parent** | <p>Selects the parent folder(s) of the currently selected layer(s).</p> |
| **Previous Selection** | <p>Goes back to the previously selected set of layers or nodes in the selection history.</p><p>If the side of your mouse has navigation buttons, you can use the back button as a shortcut (not supported in Firefox).</p> |
| **Next Selection** | <p>Goes forward to the next selected set of layers or nodes in the selection history.</p><p>If the side of your mouse has navigation buttons, you can use the forward button as a shortcut (not supported in Firefox).</p> |
### View
The **View menu** lists actions related to the view of the canvas within the viewport:
| | |
|-|-|
| **Tilt** | <p>Begins tilting the viewport angle based on your mouse movements.</p><p>While tilting, hold <kbd>Shift</kbd> to snap to 15° increments. Confirm with a left click or <kbd>Enter</kbd>. Cancel with a right click or <kbd>Esc</kbd>.</p> |
| **Reset Tilt** | <p>Sets the viewport tilt angle back to 0°.</p> |
| **Zoom In** | <p>Narrows the view to the next whole zoom increment, such as:</p><p>25%, 33.33%, 40%, 50%, 66.67%, 80%, 100%, 125%, 160%, 200%, 250%, 320%, 400%, 500%</p> |
| **Zoom Out** | <p>Widens the view to the next whole zoom increment, such as above.</p> |
| **Zoom to Selection** | <p>Zooms and frames the viewport to the bounding box of the selected layer(s).</p> |
| **Zoom to Fit** | <p>Zooms and frames the viewport to fit all artboards, or all artwork if using infinite canvas.</p> |
| **Zoom to 100%** | <p>Zooms the viewport in or out to 100% scale, making the document and viewport scales match 1:1.</p> |
| **Zoom to 200%** | <p>Zooms the viewport in or out to 200% scale, displaying the artwork at twice the actual size.</p> |
| **Flip** | <p>Mirrors the viewport horizontally, flipping the view of the artwork until deactivated.</p> |
| **Rulers** | <p>Toggles visibility of the rulers along the top/left edges of the viewport.</p> |
### Window
The **Window menu** lists actions related to the visibility of workspace panels within the application window:
| | |
|-|-|
| **Properties** | <p>Toggles visibility of the **Properties panel** on the upper-right side of the workspace. It is used to inspect and edit the values of graphics operation (node) parameters. Selected layers or nodes display their parametric controls in this panel.</p> |
| **Layers** | <p>Toggles visibility of the **Layers panel** on the lower-right side of the workspace. It is used to organize and select the artboards and layers that form the structure of a document.</p> |
| **Data** | <p>Toggles visibility of the **Data panel** on the lower-left side of the workspace. It is used to introspect data flow from the output of a selected node for technical debugging of content generated within the node graph.</p> |
### Help
The **Help menu** lists actions related to information about Graphite:
| | |
|-|-|
| **About Graphite…** | <p>Opens the **About Graphite** dialog for displaying release and license information. You can check it for the release date of the current editor version.</p> |
| **User Manual** | <p>Opens this [user manual](./learn).</p> |
| **Donate to Graphite** | <p>Opens the Graphite [development fund](/donate) page where you can contribute financially to support ongoing development of the project.</p> |
| **Report a Bug** | <p>Opens a page to file a [new GitHub issue](https://github.com/GraphiteEditor/Graphite/issues/new).</p> |
| **Visit on GitHub** | <p>Opens the [Graphite GitHub repository](https://github.com/GraphiteEditor/Graphite).</p> |
| **Developer Debug** | <p>A section with developer-only actions. Users should ignore these.</p> |
@@ -0,0 +1,20 @@
+++
title = "Introduction"
template = "book.html"
page_template = "book.html"
[extra]
order = 1
js = ["/js/component/youtube-embed.js"]
css = ["/component/youtube-embed.css"]
+++
<!-- Before taking the time to read the coming chapters, let's build some context by jumping straight into a small project that you can follow along with. That way you will have a mental framework for the topics explained in the rest of this manual. -->
Begin learning Graphite by watching the introductory tutorial video below.
One is available now, and more will be released on a regular basis throughout early 2026.
{{ youtube_embed(id="7gjUhl_3X10", title="Vector Art Quickstart - Graphite, the Open Source 2D Graphics Suite") }}
*More tutorials coming soon.*
@@ -0,0 +1,54 @@
+++
title = "Features and limitations"
[extra]
order = 1
+++
Bear in mind that Graphite is alpha software, meaning it is actively changing and improving.
## Current capabilities
A lot is planned on the future [roadmap](/features#roadmap), but here's an overview of the concepts behind the selection of workflows you can currently use in Graphite.
### Vector illustration and graphic design
<img src="https://static.graphite.art/content/learn/introduction/features-and-limitations/cactus-vector-art.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Example vector artwork of a potted cactus" style="max-width: unset; float: right; margin-left: 40px" />
Vector editing is the core competency of the Graphite editor at this stage in its development. That means you can create shape-based vector artwork and designs with the available tools.</p><p>Primitive geometry like rectangles and ellipses can be drawn and, as desired, modified into more complex shapes using the Path tool. Fully organic shapes may also be created from scratch with the Pen tool. They can then be given colors and gradients to add visual style. This cactus is an example of the style of artwork you can create with vector graphics.
### Procedural design
A procedural content generation workflow lets you describe *how* a creative decision becomes a visual outcome rather than doing it all yourself. For example, copying a shape 25 times around the inside of a circle would be tedious work if done by hand but it's easy for the computer to do it. And if you decide 10 instances may look better than 25, or you want to change the copied shape, or you opt for a different radial separation, it's easy to just update a numerical parameter. That saves you from laboriously placing every shape all over again. You're able to build a *procedure* that the computer carries out on your behalf.
The aforementioned example takes the form of the <img src="https://static.graphite.art/content/learn/introduction/features-and-limitations/circular-repeat-node__3.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" onload="this.width = this.naturalWidth / 2" style="vertical-align: middle" alt="Circular Repeat" /> node which is represented as this box-shaped entity with colored *connectors* on either end. *Nodes* encode certain operations (or functions) in the procedure that generates your artwork. Once you've drawn some content, you can see the nodes which generate it by opening the *node graph* with the <img src="https://static.graphite.art/content/learn/introduction/features-and-limitations/node-graph-button.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" onload="this.width = this.naturalWidth / 2" style="vertical-align: middle" alt="'Node Graph' button" /> button located to the top right of the viewport. This example may have a node setup which looks like this:
<p><img src="https://static.graphite.art/content/learn/introduction/features-and-limitations/path-fill-circular-repeat-layer.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Path node, Fill node, Circular Repeat node in a sequence feeding into the Untitled Layer" /></p>
Starting from the left, the <img src="https://static.graphite.art/content/learn/introduction/features-and-limitations/path-node.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" onload="this.width = this.naturalWidth / 2" style="vertical-align: middle" alt="Path" /> node generates some geometry (in this case, drawn using the *Pen* tool). Next, the vector path data feeds through the <img src="https://static.graphite.art/content/learn/introduction/features-and-limitations/fill-node__2.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" onload="this.width = this.naturalWidth / 2" style="vertical-align: middle" alt="Fill" /> node to apply a blue color. At this point, the path data looks like so:
<p><img src="https://static.graphite.art/content/learn/introduction/features-and-limitations/blue-arch-shape.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" onload="this.width = this.naturalWidth / 2" alt="" /></p>
Next, that is fed into the <img src="https://static.graphite.art/content/learn/introduction/features-and-limitations/circular-repeat-node__3.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" onload="this.width = this.naturalWidth / 2" style="vertical-align: middle" alt="Circular Repeat" /> node which has several parameters you can modify and get different output data based on your choices, like in these examples:
<style class="table-1-style">
.table-1-style + table td {
vertical-align: middle;
}
</style>
| | |
|:-:|:-:|
| <img src="https://static.graphite.art/content/learn/introduction/features-and-limitations/circular-repeat-node-parameters-1__2.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" onload="this.width = this.naturalWidth / 2" alt="" /> | <img src="https://static.graphite.art/content/learn/introduction/features-and-limitations/circular-repeat-node-output-1.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" onload="this.width = this.naturalWidth / 2" alt="" /> |
| <img src="https://static.graphite.art/content/learn/introduction/features-and-limitations/circular-repeat-node-parameters-2__2.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" onload="this.width = this.naturalWidth / 2" alt="" /> | <img src="https://static.graphite.art/content/learn/introduction/features-and-limitations/circular-repeat-node-output-2.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" onload="this.width = this.naturalWidth / 2" alt="" /> |
| <img src="https://static.graphite.art/content/learn/introduction/features-and-limitations/circular-repeat-node-parameters-3__2.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" onload="this.width = this.naturalWidth / 2" alt="" /> | <img src="https://static.graphite.art/content/learn/introduction/features-and-limitations/circular-repeat-node-output-3.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" onload="this.width = this.naturalWidth / 2" alt="" /> |
| <img src="https://static.graphite.art/content/learn/introduction/features-and-limitations/circular-repeat-node-parameters-4__2.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" onload="this.width = this.naturalWidth / 2" alt="" /> | <img src="https://static.graphite.art/content/learn/introduction/features-and-limitations/circular-repeat-node-output-4.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" onload="this.width = this.naturalWidth / 2" alt="" /> |
<!-- TODO: Rename "Angle Offset" to "Start Angle" and redo the screenshots which show that -->
The node's properties offer controls over settings like *Angle Offset* (what angle to start at), *Radius* (distance from the center), and *Instances* (how many copies to distribute). These parameters can also be exposed into the graph so they are driven by the calculated numerical outputs of other nodes instead of values you pick by hand.
### Raster compositing
Raster image editing is a growing capability that will develop over time into the central focus of Graphite. Raster imagery is composed of pixels which are grids of color that can represent anything visual, like paintings and photographs. The current feature set lets you import images, manipulate them using the node-based compositor, and apply nondestructive global effects like color adjustment filters.
A prototype <img src="https://static.graphite.art/content/learn/introduction/features-and-limitations/brush-tool-icon.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" onload="this.width = this.naturalWidth / 2" alt="" style="vertical-align: bottom" /> Brush tool exists letting you draw simple doodles and sketches. However it is very limited in its capabilities and there are multiple bugs and performance issues with the feature. It can be used in a basic capacity, but don't expect to paint anything too impressive using raster brushes quite yet. The tool will be fully rewritten in the future.
@@ -0,0 +1,225 @@
+++
title = "Graphite license"
[extra]
css = ["/layout/reading-material.css"]
+++
<section class="reading-material">
<div class="block">
# Graphite license
<article>
Graphite is open source software made by its community and distributed by *Graphite Labs, LLC*, the official [organization](/about#organization) that operates the project. Builds of the application are free to use and redistribute, even for commercial purposes. The artwork you create always belongs to you.
## Source code
The source code [available on GitHub](https://github.com/GraphiteEditor/Graphite) (including the Graphite editor application, libraries, and other software materials) is shared under the Apache License 2.0 posted below, unless otherwise noted within the repository.
---
Apache License
Version 2.0, January 2004
<http://www.apache.org/licenses/>
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
## Branding
Assets that contribute to the visual identity of the Graphite brand and software (including [logos](/logo) and icon sets) are not considered software code, and are licensed separately from the [source code license](#source-code) described above. These are kept in a [separate repository](https://github.com/Keavon/graphite-branded-assets/) because they are designed independently and are not within scope of the open source project.
Official builds of Graphite may be compiled and redistributed freely under the terms of the Graphite Branding License, but derivative works (i.e. forks) must substitute these assets before distributing modified releases of the software. This protects the integrity of the brand and encourages developers to [contribute](/volunteer) back in support of the official community-based project instead of fragmenting the ecosystem with lookalikes.
---
Graphite Branding License
Copyright © 2021-2026 Graphite Labs, LLC. All rights reserved.
The repository `https://github.com/Keavon/graphite-branded-assets/` includes proprietary assets (the "Assets"), including but not limited to logos, icons, and branding materials. The Assets are not software source code and are consequently not licensed under the same terms as the other works of Graphite software materials. The intention of this license is to maintain the full permissiveness of the software code while reserving protections for the brand and visual identity of the official Graphite product.
Permission is granted to use, reproduce, and distribute the Assets solely as part of unmodified build artifacts produced from the official Graphite repository at `https://github.com/GraphiteEditor/Graphite`, on master branch commits, using the official build process as documented therein. Any other use of the Assets—including copying, extraction, incorporation into modified or forked builds, or use in other projects or contexts—is prohibited without prior written permission from the copyright holder.
This license does not grant any rights under trademark law or affect the licensing of the software source code.
THE ASSETS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY ARISING FROM THE USE OF THE ASSETS.
This license is automatically terminated if the terms herein are violated.
</article>
</div>
</section>
+101
View File
@@ -0,0 +1,101 @@
+++
title = "Graphite logo"
[extra]
css = ["/page/logo.css", "/component/feature-box.css", "/layout/reading-material.css"]
+++
<section class="reading-material">
<div class="block">
# Graphite logo
<article>
Graphite's logo is represented by the end of a pencil, drawing a sketch mark, that is protruding from a hexagon.
The pencil and its streak, composed of graphite (the substance), signifies the software's name and its role as a drawing tool and versatile art medium.
The hexagon represents one unit in the lattice grid chemical structure of graphene. The angled line is a double bond. This references the Graphene node engine which powers the Graphite editor. The lattice grid also resembles the nodes and edges of a mathematical graph, referencing the node graph that is fundamental to both the Graphene engine and Graphite editor. The first syllable of both names also alludes to that central node graph.
## Usage policy
The Graphite logo is made available for community use, with some limitations. While the software is free and open source, the brand identity is more restrictive. Please be respectful of the Graphite brand by reviewing this usage policy.
Be aware that the logo is not covered under the [Apache License 2.0](/license#source-code) of Graphite's source code. Its usage within Graphite software distributions is covered by the [Graphite Branding License](/license#branding).
Outside the context of the Graphite application (or derivatives), community usage of the logo must adhere to the usage policy:
1. Do not use the Graphite logo as your own. It should not be used as your primary—or most visually prominent—branding, and may never be incorporated into your own logo. Your usage should not imply that it's a part of the official Graphite project, nor that it's endorsed or affiliated.
2. Your (product, service, website, company, channel, etc.) brand name should not begin with "Graphite", or use it as the most prominent word in the name. It may only be used as a secondary or descriptive word that comes after your own unique name. For example: "XYZ for Graphite" is acceptable, but "Graphite XYZ" is not since it implies affiliation.
3. Only use the logo when talking about, describing, referencing, or crediting the official Graphite project or software. If used as a hyperlink, it should point only to <https://graphite.art>.
4. Do not modify the logo. The solid-colored version may only be recolored with another substitute solid color if doing so is necessary for visual consistency when presented alongside other logos of the same color. Sufficient blank space should be preserved around the logo so it does not overlap or compete with impinging design elements.
5. Commercial use of the logo (for example, merchandise sales) is not allowed without express written permission.
If in doubt, please <a href="/contact">get in touch</a> by email to request clarification or permission.
## Download
Download the complete [logo kit](https://static.graphite.art/logos/graphite-logo-kit.zip) or a specific version in PNG or SVG format below.
</article>
</div>
</section>
<section class="feature-box-outer logo-view color">
<div class="feature-box-inner">
<div>
<img src="https://static.graphite.art/logos/graphite-logotype-color.svg" height="160" />
<span>Graphite Logotype (Color)</span>
<a href="https://static.graphite.art/logos/graphite-logotype-color.svg" download>Download (SVG)</a>
<a href="https://static.graphite.art/logos/graphite-logotype-color-240x937.png" download>Download (PNG 240x937)</a>
<a href="https://static.graphite.art/logos/graphite-logotype-color-480x1874.png" download>Download (PNG 480x1874)</a>
</div>
<div>
<img src="https://static.graphite.art/logos/graphite-logo-color.svg" width="160" height="160" />
<span>Graphite Icon (Color)</span>
<a href="https://static.graphite.art/logos/graphite-logo-color.svg" download>Download (SVG)</a>
<a href="https://static.graphite.art/logos/graphite-logo-color-240x240.png" download>Download (PNG 240x240)</a>
<a href="https://static.graphite.art/logos/graphite-logo-color-480x480.png" download>Download (PNG 480x480)</a>
</div>
</div>
</section>
<section class="feature-box-outer logo-view light">
<div class="feature-box-inner">
<div>
<img src="https://static.graphite.art/logos/graphite-logotype-solid.svg" height="160" />
<span>Graphite Logotype (Solid)</span>
<a href="https://static.graphite.art/logos/graphite-logotype-solid.svg" download>Download (SVG)</a>
<a href="https://static.graphite.art/logos/graphite-logotype-solid-240x937.png" download>Download (PNG 240x937)</a>
<a href="https://static.graphite.art/logos/graphite-logotype-solid-480x1874.png" download>Download (PNG 480x1874)</a>
</div>
<div>
<img src="https://static.graphite.art/logos/graphite-logo-solid.svg" width="160" height="160" />
<span>Graphite Icon (Solid)</span>
<a href="https://static.graphite.art/logos/graphite-logo-solid.svg" download>Download (SVG)</a>
<a href="https://static.graphite.art/logos/graphite-logo-solid-240x240.png" download>Download (PNG 240x240)</a>
<a href="https://static.graphite.art/logos/graphite-logo-solid-480x480.png" download>Download (PNG 480x480)</a>
</div>
</div>
</section>
<section class="feature-box-outer logo-view dark">
<div class="feature-box-inner">
<div>
<img src="https://static.graphite.art/logos/graphite-logotype-solid-white.svg" height="160" />
<span>Graphite Logotype (Solid, White)</span>
<a href="https://static.graphite.art/logos/graphite-logotype-solid-white.svg" download>Download (SVG)</a>
<a href="https://static.graphite.art/logos/graphite-logotype-solid-white-240x937.png" download>Download (PNG 240x937)</a>
<a href="https://static.graphite.art/logos/graphite-logotype-solid-white-480x1874.png" download>Download (PNG 480x1874)</a>
</div>
<div>
<img src="https://static.graphite.art/logos/graphite-logo-solid-white.svg" width="160" height="160" />
<span>Graphite Icon (Solid, White)</span>
<a href="https://static.graphite.art/logos/graphite-logo-solid-white.svg" download>Download (SVG)</a>
<a href="https://static.graphite.art/logos/graphite-logo-solid-white-240x240.png" download>Download (PNG 240x240)</a>
<a href="https://static.graphite.art/logos/graphite-logo-solid-white-480x480.png" download>Download (PNG 480x480)</a>
</div>
</div>
</section>
@@ -0,0 +1,30 @@
+++
title = "Press resources"
[extra]
css = ["/layout/reading-material.css"]
+++
<section class="reading-material">
<div class="block">
# Press resources
<article>
Materials for journalists and creators looking to share Graphite with their audiences.
## Contact
Please get in touch if you'd like to conduct an interview or have questions answered.
Send an email to [contact<wbr>@graphite<wbr>.art](mailto:contact@graphite.art) and you can usually expect a quick reply.
## Logo
Logos in PNG and SVG format are available for use in articles talking about Graphite. See the [logo](/logo) page for downloads.
</article>
</div>
</section>
@@ -0,0 +1,24 @@
+++
title = "Graphite privacy policy"
[extra]
css = ["/layout/reading-material.css"]
+++
<section class="reading-material">
<div class="block">
# Graphite privacy policy
<article>
The Graphite application does not require user accounts and does not collect, store, or transmit personal information. All user data is stored locally on-device and the application functions primarily offline.
The application makes limited network requests for functionality such as retrieving font metadata and checking for updates. These API requests may include basic, non-identifying technical information (such as app version and platform) to serve appropriate resources. Network requests are subject to standard server-side analytics used to operate the service, which collect only aggregated, anonymized usage statistics and are not used for tracking individual users.
Graphite does not engage in the activities of selling, sharing, or advertising with personal data. As a free and open-source software project, the application is designed with privacy as a guiding principle.
</article>
</div>
</section>
@@ -0,0 +1,124 @@
+++
title = "Volunteer"
[extra]
css = ["/page/volunteer.css", "/component/feature-box.css"]
+++
<section>
<div class="block">
# Get involved
**Graphite is 100% built by volunteers.** Get involved in the effort to bring great, free creative software to the world.
</div>
</section>
<section>
## Code contributions
<div class="feature-box-narrow">
<a href="/volunteer/guide">
<img src="https://static.graphite.art/content/volunteer/code-contributions.avif" class="feature-box-full-image" style="aspect-ratio: 3/1 auto; background: var(--color-seaside)" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.jpg')" alt="Flavor graphic depicting a library of knowledge in a digital realm" />
</a>
Get started by reading the contributor guide:
<a href="/volunteer/guide" class="button arrow">Contributor guide</a>
</div>
<div class="diptych code-contributions">
<div class="block feature-box-narrow">
<h1 class="feature-box-header">Editor team</h1>
The Graphite editor is built much like a game engine, split across user interface application tooling and a renderer with nodes implementing an assortment of graphics algorithms.
</div>
<div class="block feature-box-narrow">
<h1 class="feature-box-header">Compiler team</h1>
[Graphene](/volunteer/guide/graphene) is a programming language, interpreter, and runtime environment built upon Rust which enables Graphite artwork to compile to executable programs for fast rendering.
</div>
</div>
</section>
<section>
## Creative contributions
<div class="feature-box-narrow">
<img src="https://static.graphite.art/content/volunteer/creative-contributions.avif" class="feature-box-full-image" style="aspect-ratio: 3/1 auto; background: var(--color-lemon)" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.jpg')" alt="Flavor graphic depicting a fountain pen, ink pots, and a book" />
</a>
Assign yourself the *"🙌 Interested in helping with art or marketing"* role in the *#welcome* Discord channel. Then mention your experience and how you'd like to help in the *#introductions* channel.
<a href="https://discord.graphite.art" class="button arrow">Volunteer on Discord</a>
</div>
<div class="diptych creative-contributions">
<div class="block feature-box-narrow">
<h1 class="feature-box-header">Art team</h1>
Use your artistic talents to conceptualize and produce high-quality open art projects published by the Graphite project to stress-test and showcase the editor's capabilities.
</div>
<div class="block feature-box-narrow">
<h1 class="feature-box-header">Marketing team</h1>
Help write, edit, and design content for this website, social media, newsletters, blog posts, user manual pages, videos, fundraising campaigns, press releases, and industry outreach.
</div>
</div>
</section>
<section>
## User contributions
<div class="feature-box-narrow">
<img src="https://static.graphite.art/content/volunteer/user-contributions.avif" class="feature-box-full-image" style="aspect-ratio: 3/1 auto; background: var(--color-lilac)" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.jpg')" alt="Flavor graphic depicting a magnifying glass on the search for a software bug" />
Assign yourself the *"🐒 Volunteer to get pinged regularly for QA testing"* or *"🤖 Interested in contributing code"* roles in the *#welcome* Discord channel. In the latter case, drop by the *#development* channel to get advice writing your first node.
<a href="https://discord.graphite.art" class="button arrow">Volunteer on Discord</a>
</div>
<div class="diptych user-contributions">
<div class="block feature-box-narrow">
<h1 class="feature-box-header">QA team</h1>
Get familiar with the ins-and-outs of the editor and respond actively to developer requests on a recurring basis to test out new features and find bugs and breakages.
</div>
<div class="block feature-box-narrow">
<h1 class="feature-box-header">Nodes team</h1>
Explore and push the limits of the node graph with complex procedural designs. Report your findings about limitations, opportunities, and use cases to help in designing new nodes.
</div>
</div>
</section>
@@ -0,0 +1,29 @@
+++
title = "Contributor guide"
template = "book.html"
page_template = "book.html"
aliases = ["/contribute"]
[extra]
book = true
+++
Welcome, potential contributor! We're excited to have you join the Graphite project. It is our goal to make the process as smooth as possible. This guide will serve as your library of knowledge to help you get started contributing to the project. If you find any information missing or unclear, please let us know through Discord or submit a pull request to help document the process for future contributors.
## Required experience level
Our aim is to make the process as accessible as possible for first-time Graphite contributors with solid computer science foundations to get started, even without prior open source experience. Many contributors have remarked that they found our process notably more welcoming and straightforward than open source projects they've previously tried contributing to.
However, please bear in mind that developing Graphite is **not for beginner programmers**. It is a complex, cutting-edge software engineering endeavor that requires at least a strong grasp of programming principles (although not necessarily Rust; you can learn that as you go).
If you are at least a junior-level developer or a self-motivated student who has built a number of self-directed, non-trivial personal projects (especially if they involve graphics), then you should be well-prepared to contribute to Graphite. If you are only a couple years into your programming journey, be warned that you will find Graphite frustratingly above your current skill level and we will be unable to support you adequately. The Graphite project is **not a learning platform or a classroom for inexperienced developers**, but with the requisite skills, it is a great way to develop real-world engineering experience and get involved in something meaningful.
Do not make the mistake of assuming AI tools and agents can be a substitute for your own skills and experience. If you use AI tools in your workflow, read our [AI contribution policy](./starting-a-task/ai-contribution-policy).
## Discord communication
The next page will cover how to compile the Graphite source code. But first, make sure you've joined our [Discord server](https://discord.graphite.art) and assigned yourself the *"🤖 Interested in contributing code"* role from the `#🙂welcome` channel. (And after your first PR is accepted and merged, you should post a request in `#📄development` to be assigned the *"Code Contributor"* role.)
This is semi-mandatory, particularly if you intend to become a regular contributor on the team, because it is how our team communicates and is just as central to our process as GitHub.
Done that? Alright, proceed to the next page!
@@ -0,0 +1,61 @@
+++
title = "Codebase overview"
template = "book.html"
page_template = "book.html"
[extra]
order = 2 # Chapter number
js = ["/js/component/youtube-embed.js", "/js/page/contributor-guide/crate-hierarchy.js"]
css = ["/component/youtube-embed.css", "/page/contributor-guide/crate-hierarchy.css"]
+++
The best introduction for getting up-to-speed with Graphite contribution comes from watching this webcast recording. Before asking questions in Discord, please watch the full video because it gives a comprehensive overview of most things you will need to know.
{{ youtube_embed(id="vUzIeg8frh4", title="Workshop: Intro to Coding for Graphite") }}
## Codebase structure
Graphite is built from several main software components. New developers may choose to specialize in one or more area without having to attain a working knowledge of the full codebase.
### Frontend
*Location: [`/frontend/src`](https://github.com/GraphiteEditor/Graphite/tree/master/frontend/src)*
The frontend is the GUI for Graphite which users see and interact with. It is built using web technologies with TypeScript and Svelte (HTML and SCSS). The frontend's philosophy is to be as lightweight and minimal as possible. It acts as the entry point for user input and then quickly hands off its work to the WebAssembly editor backend via its [Wasm wrapper API](https://github.com/GraphiteEditor/Graphite/tree/master/frontend/wrapper). That API is written in Rust but has TypeScript bindings generated by the [wasm-bindgen](https://github.com/rustwasm/wasm-bindgen) tooling that is part of the Vite-based build toolchain. The frontend is built of many [components](https://github.com/GraphiteEditor/Graphite/tree/master/frontend/src/components) that recursively form the window, panels, and widgets that make up the user interface.
### Editor
*Location: [`/editor`](https://github.com/GraphiteEditor/Graphite/tree/master/editor)*
[The editor](./editor-structure) is the core of the Graphite application, and it's where all the business logic occurs for the tooling and user interaction. It is written in Rust and compiled to WebAssembly. At its heart is the message system. It is responsible for communicating with Graphene as well as handling the actual logic, state, tooling, and responsibilities of the interactive application.
### Graphene
*Location: [`/node-graph`](https://github.com/GraphiteEditor/Graphite/tree/master/node-graph)*
[Graphene](../graphene/) is the node graph engine which manages and renders the documents. It is itself a programming language, where Graphene programs are compiled while being edited live by the user, and where executing the program renders the document.
## Crate dependency graph
```sh
# Access this quickly in the future:
cargo run explore deps
```
This diagram shows the structure of the crates that comprise the Graphite codebase and how they depend on each other. Every Arrow points from a crate to another which it depends on.
<div class="crate-hierarchy">
<!-- replacements::crate_hierarchy() -->
</div>
## Frontend/backend communication
Frontend-to-backend communication is achieved through a thin Rust translation layer in [`/frontend/wrapper/src/editor_wrapper.rs`](https://github.com/GraphiteEditor/Graphite/tree/master/frontend/wrapper/src/editor_wrapper.rs) which wraps the editor backend's Rust-based message system API and provides the TypeScript-compatible API of callable functions. These wrapper functions are compiled by [wasm-bindgen](https://github.com/rustwasm/wasm-bindgen) into autogenerated TS functions that serve as an entry point from TS into the Wasm binary.
Backend-to-frontend communication happens by sending a queue of messages to the frontend message dispatcher. After the TS has called any wrapper API function to get into backend code execution, the editor's business logic runs and queues up each [`FrontendMessage`](https://github.com/GraphiteEditor/Graphite/tree/master/editor/src/messages/frontend/frontend_message.rs) which get mapped from Rust to JavaScript data structures in [`/frontend/src/messages.ts`](https://github.com/GraphiteEditor/Graphite/tree/master/frontend/src/messages.ts). Various TS code subscribes to these messages by calling:
```rs
subscribeFrontendMessage(NameOfMessage, (messageData) => { /* callback code */ });
```
@@ -0,0 +1,112 @@
+++
title = "Debugging tips"
[extra]
order = 2 # Page number after chapter intro
css = ["/page/contributor-guide/bisect-tool.css"]
js = ["/js/page/contributor-guide/bisect-tool.js"]
+++
The Wasm-based editor has some unique limitations about how you are able to debug it. This page offers tips and best practices to get the most out of your problem-solving efforts.
## Comparing with deployed builds
When tracking down a bug, first check if the issue you are noticing also exists in `master` or just in your branch. Open up [dev.graphite.art](https://dev.graphite.art) which always deploys the lastest commit, as opposed to [editor.graphite.art](https://editor.graphite.art) which deploys the latest stable release. Build links for any commit may be found by clicking the "comment" icon on the right side of any commit in the GitHub repo [commits list](https://github.com/GraphiteEditor/Graphite/commits/master/).
Use *Help* > *About Graphite…* in the editor to view any build's Git commit hash.
Beware of a potential pitfall: all deploys and build links are built with release optimizations enabled. This means some bugs (like crashes from bounds checks or debug assertions) may exist in `master` and would appear if run locally, but not in the deployed version.
## Build bisect tool
```sh
# Access this quickly in the future:
cargo run explore bisect
```
This interactive tool helps you binary search through recent commits, test the build links of each, and pinpoint which change introduced a regression or added a feature.
<div class="bisect-tool">
<div class="phase active" data-phase="setup">
<div class="setup-section">
<div class="section-label">
<span><strong>What are you looking for?</strong></span>
</div>
<label>
<input type="radio" name="bisect-mode" value="regression" checked />
<span>Find when a regression or bug started</span>
</label>
<label>
<input type="radio" name="bisect-mode" value="feature" />
<span>Find when a feature was added or fixed</span>
</label>
</div>
<div class="setup-section">
<div class="section-label">
<span><strong>When do you estimate this changed?</strong></span>
</div>
<label>
<input type="radio" name="start-method" value="date" checked />
<span>Date</span>
</label>
<label>
<input type="radio" name="start-method" value="hash" />
<span>Commit</span>
</label>
</div>
<div class="commit-inputs">
<div class="start-input" data-input="date">
<input type="date" data-commit-date />
</div>
<div class="start-input hidden" data-input="hash">
<input data-commit-hash placeholder="Commit hash" pattern="[0-9a-fA-F]{7,40}" />
</div>
<span class="button arrow" data-start-button>Begin bisect</span>
</div>
</div>
<div class="phase" data-phase="bisect">
<div class="block feature-box-narrow">
<div class="step-header">
<span class="step-label" data-step-label><strong>Bisect step 1</strong></span>
<span class="go-back hidden" data-go-back-button>(<a>go back</a>)</span>
</div>
<div class="progress-info" data-progress-info></div>
<div class="commit-info" data-commit-info></div>
<span class="button arrow" data-test-build-button>Test this build</span>
<span class="findings">After testing, what have you found?</span>
<div class="bisect-actions">
<span class="button" data-issue-present-button></span>
<span class="button" data-issue-absent-button></span>
</div>
</div>
</div>
<div class="error-message" data-message-box></div>
</div>
## Printing to the console
Use the browser console (<kbd>F12</kbd>) to check for warnings and errors. In Rust, use `log::debug!("The number is {some_number}");` to print to the browser console. These statements should be for temporary debugging. Remove them before your code is reviewed. Print-based debugging is necessary because breakpoints are not supported in WebAssembly.
Additional print statements are available that *should* be committed:
- `log::error!()` is for descriptive user-facing error messages arising from a bug
- `log::warn!()` is for non-critical problems that likely indicate a bug somewhere
- `log::trace!()` is for verbose logs of ordinary internal activity, hidden by default but viewable by activating *Help* > *Debug: Print Trace Logs*
## Message system logs
To also view logs of the messages dispatched by the message system, activate *Help* > *Debug: Print Messages* > *Only Names*. Or use *Full Contents* for a more verbose view containing the actual data being passed. This is an invaluable window into the activity of the message flow and works well together with `log::debug!()` printouts for tracking down message-related defects.
## Node/layer and document IDs
In debug mode, hover over a layer's name in the Layers panel, or a layer/node in the node graph, to view a tooltip with its ID. Likewise, document IDs may be read from their tab tooltips.
## Performance profiling
Be aware that having your browser's developer tools open will significantly impact performance in both debug and release builds, so it's best to close that when not in use.
The *Performance* tab of the browser developer tools lets you record and analyze performance profiles, and this is a useful way to track down bottlenecks. The Firefox profiler has some additional features missing from the Chromium debugger, so if you are digging deep into a performance issue, it can be worth giving Firefox a try for that purpose. Be sure to use debug builds while profiling, otherwise inlined functions and other optimizations may produce a misleading view of where time is being spent. The live deployed web app (production and dev) and build links hosted by our CI infrastructure are all built with release optimizations.
@@ -0,0 +1,103 @@
+++
title = "Editor structure"
[extra]
order = 1 # Page number after chapter intro
css = ["/page/contributor-guide/editor-structure.css"]
js = ["/js/page/contributor-guide/editor-structure.js"]
+++
The Graphite editor is the application users interact with to create documents. Its code is a single Rust crate that lives below the frontend (web code) and above [Graphene](../../graphene) (the node-based graphics engine). The main business logic of all visual editing is handled by the editor backend. When running in the browser, it is compiled to WebAssembly and passes messages to the frontend.
## Message system
The Graphite editor backend is organized into a hierarchy of subsystems which talk to one another through message passing. Messages are pushed to the front or back of a queue and each one is processed sequentially by the editor's dispatcher.
The dispatcher lives at the root of the editor hierarchy and acts as the owner of all its top-level message handlers. This satisfies Rust's restrictions on mutable borrows because only the dispatcher may mutate its message handlers, one at a time, while each message is processed.
## Editor outline
```sh
# Access this quickly in the future:
cargo run explore editor
```
Click to explore the outline of the editor subsystem hierarchy which forms the structure of the editor's subsystems, state, and interactions. Also available as a searchable <a href="/volunteer/guide/codebase-overview/hierarchical-message-system-tree.txt">plain text file</a>.
<div class="structure-outline">
<!-- replacements::hierarchical_message_system_tree() -->
</div>
### Parts of the hierarchy
<span class="subsystem">Subsystem components</span>
- A <span class="subsystem">*Message</span> enum is the component of an editor subsystem that defines its message interfaces as enum variants. Messages are used for passing a request from anywhere in the application, optionally with some included data, to have a particular block of code be run by its respective message handler.
- A <span class="subsystem">*MessageHandler</span> struct is the component of an editor subsystem that has ownership over its persistent editor state and its child message handlers for the lifetime of the application. It also defines the logic for handling each of its messages that it receives from the dispatcher. Those blocks of logic may further enqueue additional messages to be processed by itself or other message handlers during the same dispatch cycle.
- A <span class="subsystem">*MessageContext</span> struct is the component of an editor subsystem that defines what data is made available from other subsystems when running the logic to handle a dispatched message. It is a struct that is passed to the message handler when processing a message, and it gets filled in with data (owned, borrowed, or mutably borrowed) from its parent message handler. Intermediate subsystem layers may forward data from their parent to their child contexts to make state available from further up the hierarchy.
<span class="submessage">Sub-messages</span>
- A <span class="submessage">#[child] *</span> attribute-decorated message enum variant is a special kind of message that encapsulates a nested subsystem. As with all messages, its handler has a manually written code block. But that code must call its corresponding child message handler's `process_message` method. The child message handler is a field of this parent message handler's state struct.
`Messages`
- A `*` message enum variant is used throughout the editor to request that a certain subsystem performs some action, potentially given some data. In that sense, it resembles a function call, but a key difference is that messages are queued up and processed sequentially in a flat order, always invoked by the dispatcher.
## How messages work
Messages are enum variants that are dispatched to perform some intended activity within their respective message handlers. Here are two message definitions from <span class="subsystem">DocumentMessage</span>:
```rs
pub enum DocumentMessage {
...
// A message that carries one data field
DeleteLayer {
id: NodeId,
}
// A message that carries no data
DeleteSelectedLayers,
...
}
```
As shown above, additional data fields can be included with each message. But as a special case denoted by a <span class="submessage">#[child]</span> attribute, that data can also be a sub-message enum, which enables hierarchical nesting of message handler subsystems.
By convention, regular data must be written as struct-style named fields (shown above), while a sub-message enum must be written as a tuple/newtype-style field (shown below). The <span class="subsystem">DocumentMessage</span> enum of the previous example is defined as a child of <span class="subsystem">PortfolioMessage</span> which wraps it like this:
```rs
pub enum PortfolioMessage {
...
// A message that carries the `DocumentMessage` child enum as data
#[child]
Document(DocumentMessage),
...
}
```
Likewise, the <span class="subsystem">PortfolioMessage</span> enum is wrapped by the top-level <span class="subsystem">Message</span> enum. The dispatcher operates on the queue of these base-level <span class="subsystem">Message</span> types.
So for example, the `DeleteSelectedLayers` message mentioned previously will look like this as a <span class="subsystem">Message</span> data type:
```rs
Message::Portfolio(
PortfolioMessage::Document(
DocumentMessage::DeleteSelectedLayers
)
)
```
Writing out these nested message enum variants would be cumbersome, so that <span class="submessage">#[child]</span> attribute shown earlier invokes a proc macro that automatically implements the `From` trait, letting you write this instead to get a <span class="subsystem">Message</span> data type:
```rs
DocumentMessage::DeleteSelectedLayers.into()
```
Most often, this is simplified even further because the `.into()` is called for you when pushing a message to the queue with `.add()` or `.add_front()`. So this becomes as simple as:
```rs
responses.add(DocumentMessage::DeleteSelectedLayers);
```
The `responses` message queue is composed of <span class="subsystem">Message</span> data types, and thanks to this system, child messages like `DocumentMessage::DeleteSelectedLayers` are automatically wrapped in their ancestor enum variants to become a <span class="subsystem">Message</span>, saving you from writing the verbose nested form.
@@ -0,0 +1,106 @@
+++
title = "Graphene"
template = "book.html"
page_template = "book.html"
[extra]
order = 5 # Chapter number
+++
Graphene is the node graph engine that powers the Graphite editor.
It's hard to describe in one sentence precisely what Graphene is, because it's a technology that serves several roles when viewed from different angles. But to get a feel for what it encompasses, here is a list of some of its purposes:
- Render engine
- Runtime environment
- Procedural data processor
- Node-based scripting system
- Compiled programming language
- Compiler toolchain built around `rustc`
## Background
### Artwork as a program
Artwork created in Graphite is represented as a node graph that generates the graphical content authored by the user. This document is essentially source code for a program in the Graphene language. Modifying the graph (like adding a layer, changing a node's parameter, or updating a node's data every frame while interactively drawing a shape) changes the actual program that generates and renders the artwork. This program must be recompiled and executed every frame a change is made.
Nodes are functions that run algorithms related to graphical operations. Some may read bitmap images from disk, others may generate procedural patterns, and more may be used for compositing and blending. Vector nodes can also produce shapes, alter their geometry, and apply styling and effects. Put together, a full document is built from just its interconnected nodes— producing a complete work of art generated entirely with algorithms and data.
### Graph executors as programming languages
Every node-based application needs to run its node graph to compute the resulting data. Execution occurs in an order that depends on the shape of the graph so that every node has the data it needs to compute its output.
A procedural graph executor, in its basic form, is a simple system that executes functions in the appropriate order. It feeds information between nodes and caches that data for reuse between executions so that only changed branches of the graph have to be computed again. The system, as described, is the approach commonly used by virtually all node-based apps.
Crucially, the execution flow is handled at runtime so there is some overhead during every run. By analogy to programming languages, this traditional execution model acts like an interpreted language. But interpreted languages are famously slow, and we don't want Graphite leaving performance on the table.
In designing Graphene, we decided to take a more advanced approach that could yield many of the benefits of a compiled language— code inlining, compiler optimizations, and a philosophy of offloading invariant enforcement to the type system. Instead of building a simple graph interpreter where functions (nodes) are run as user input changes, we designed a system that dynamically executes the graph with a variable degree of pre-compiled optimizations where bits and pieces are recompiled and patched in while the user modifies the artwork (and graph) every frame. Thereby, Graphene can dynamically range between an interpreted language, a JIT-optimized language, and a fully compiled language.
## Technical overview
### The latency/performance tradeoff
While working in Graphite, multiple needs arise for speed in different contexts. While making interactive changes, the user needs feedback as quickly as possible. While panning and zooming the canvas or playing an animation, the user cares about smoothness and responsiveness. When procedural artwork is exported as a standalone program that processes data at runtime (like as part of an image processing web server or embedded within a game engine), performance is the sole concern.
This sliding scale of latency/performance concerns maps directly to programming language concepts. Interpreted languages run immediately, but with slow runtime performance. JIT-optimized languages also run nearly without delay, but with less overhead than an interpreter since it can dynamically balance its effort towards optimizing and executing code. Compiled languages take upfront time to compile, but run with less overhead. A choice of optimization levels can be applied to further trade initial compilation time for runtime performance.
We designed Graphene to operate in all three regimes:
| Regime | Usage |
|-|-|
| Interpreted | While editing. Simple and currently the only mode that's implemented. |
| JIT | While editing. Dynamically bridges the gap between both other regimes by selectively substituting branches of the graph with interpreted and compiled nodes to keep latency low and work towards higher execution performance. |
| Compiled | When exported. The entire graph is compiled as a standalone program. |
### Building upon the Rust compiler
Nodes are functions written in Rust and every node has precompiled bytecode that ships with Graphite for use in the interpreted regime. The graph `input` → `A` → `B` → `C` → `output` is equivalent to the Rust statement `let output = C(B(A(input)));`. Graphene can either execute `A`, `B`, and `C` sequentially in its interpreted regime, or its JIT and compiled regimes can generate that Rust statement and compile it with the Rust compiler, `rustc`. The inlined and optimized bytecode can then be substituted for those three nodes in the JIT regime.
Graphene figures out which branches of the graph to compile and substitute as part of the JIT process while the user is authoring content in Graphite. While editing the graph, as changes occur to specific nodes, their surrounding graph branches drop back down to using the slower interpreted nodes. Then the JIT system works its way back up to faster execution over time by gradually compiling and swapping in larger optimized parts of the overall graph.
The fully compiled regime is used only when the user exports the procedural artwork as a standalone program. For example, a CLI program may read a string input argument (like a name) and procedurally generate an output image file (like a birthday card).
### Compile server
The three regimes have thus far been only a description of the eventual architecture direction. The interpreted regime is currently the only mode implemented in Graphene. The other two will require access to `rustc` which will necessitate the compile server that we will finish building and then publicly host for Graphite users in the future. Users of the desktop version of Graphite will be able to use an embedded `rustc` if the user has opted to download the Rust toolchain while installing Graphite.
Without a compile server, all the nodes are precompiled when Graphite is built. The node registry (in the file `node_registry.rs`) currently exists to allow the interpreted executor to find the Rust functions that correspond to each node with its appropriate type signature. Nodes support generics, so it's currently necessary to list every forseeable concrete type signature in the registry until the compile server can generate bytecode for less common type combinations on-the-fly.
### GPU compute shaders
Further building upon the Rust compiler toolchain, we employ the [`rust-gpu`](https://github.com/EmbarkStudios/rust-gpu) compiler backend for `rustc` which generates compute shaders that get executed on the GPU. This means we can write the same code to implement nodes that run on both CPU and GPU. (Although in practice, some nodes may need GPU-specific versions suited for the architectural limitations of GPU programming.) And we don't have to use a separate shader language!
### A language within a language
While Graphene is a programming language, it is also foundationally built upon the Rust language. We don't just use the Rust compiler, but we also employ its type system, traits, data structures, standard library, and crate ecosystem. The data that flows between nodes are Rust types (like structs, enums, tuples, primitives, and collections). Graphene's generic type system uses Rust's trait definitions in its enforcement of type safety and type inference.
### Graphene language concepts
Since Graphene is fundamentally a programming language, throughout this documentation we will use analogies which correlate Graphene concepts with their counterparts from traditional programming language theory. Here is an at-a-glance overview:
| Graphene concept | Programming language concept |
|:------------------|:-------------------------------------|
| Node | Function |
| Graphite editor | IDE/text editor |
| Document | Source code |
| Graph/network | Abstract syntax tree (AST) |
| Graph compilation | Linking/JIT optimization/compilation |
| Graph execution | Program execution |
<!-- Our philosophy of building (bootstrapping) our own higher-level language features from the language itself -->
<!-- Call arguments, construction arguments, `.eval()`, recompiling when construction argument values are updated but not when call argument data changes -->
<!-- Extract/inject nodes and metaprogramming -->
<!-- Cache nodes and stable node IDs -->
<!-- Graph rewriting step (currently used only to remove Identity nodes),
at various points in the compilation process,
based on rules akin to an optimizing compiler -->
<!-- Borrow tree -->
<!-- Document nodes, proto nodes, and networks (must be: acyclic) -->
<!-- Lambdas -->
<!-- Graph compilation process -->
<!-- The compilation server -->
<!-- Code structure overview -->
<!-- Guide for implementing a node -->
<!-- The `Node` trait -->
<!-- Generics, type inference, type erasure, and the node registry -->
<!-- Monitor nodes -->
@@ -0,0 +1,24 @@
+++
title = "Networks and nodes"
[extra]
order = 1 # Page number after chapter intro
+++
In Graphite, users build their artwork by connecting nodes together in a graph. When they want to organize and reuse a complex group of nodes, those may be encapsulated together as a subgraph in which one parent node represents the functionality of its children. In fact, many of the nodes provided in Graphite are themselves subgraphs built out of other nodes.
Double-clicking on nodes backed by a subgraph will display the subgraph's interior. Double-clicking nodes that are, instead, backed directly by Rust source code will open a code editor.
Any (sub)graph can import/export data from/to the outside world. For example, a reusable subgraph may receive an imported image then use several nodes to process it and finally export the result. Or the root-level artwork graph may import the animation timestamp and render a frame of the artwork then export it to the canvas.
In the Graphite editor UI, here is an example graph of artwork that imports no data but exports its content to the canvas:
<img src="https://static.graphite.art/content/features/mockup-node-graph.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Node graph UI mockup" />
The graph shown above represents the full artwork, meaning it's the root-level graph in its document. But there is nothing special about that graph compared to any subgraph. To avoid the confusion of calling it a graph or subgraph which comes with implications about user-facing concepts in the context of a document, we will use the less-ambiguous term **network** in the context of Graphene's internal concepts and codebase.
## Networks
A node network can be thought of as a box containing a finite set of nodes that are connected together as a directed acyclic graph (DAG). The network is only concerned with its own node-to-node data flow. But to interact with the outside world, data can be imported into the network and exported out of it. From the inside, those imported/exported data sources/destinations are connected to the other nodes in the network. From the outside, a network can be considered a "black box" that is simply fed inputs and can be executed to produce outputs.
***More coming soon...***
@@ -0,0 +1,53 @@
+++
title = "Project setup"
template = "book.html"
page_template = "book.html"
[extra]
order = 1 # Chapter number
+++
To begin working with the Graphite codebase, you will need to set up the project to build and run on your local machine. Development usually involves running the dev server which watches for changes to frontend (web) and backend (Rust) code and automatically recompiles and reloads the Graphite editor in your browser.
## Dependencies
Graphite is built with Rust and web technologies, which means you will need to install:
- [Rust](https://www.rust-lang.org/) (the latest stable release)
- [Node.js](https://nodejs.org/) (the latest LTS version)
- [Git](https://git-scm.com/) (any recent version)
## Repository
Clone the project to a convenient location:
```sh
git clone https://github.com/GraphiteEditor/Graphite.git
```
## Development builds
In the project directory, run the build system by executing:
```sh
cargo run
```
This will check for the required system dependency versions, help you install any that are missing, and spin up the dev server at <http://localhost:8080> serving the web app with debug optimizations. A file watcher hot-reloads the web app when you save a code file. Shut down the dev server by double pressing <kbd>Ctrl</kbd><kbd>C</kbd>.
For additional build commands, see:
```sh
cargo run help
```
For example, if you must proxy the dev server connection over a slow network where the >100 MB unoptimized binary size would pose an issue, you may need to run with release optimizations using `cargo run release`.
## Development tooling
We provide default configurations for VS Code users. When you open the project, watch for a prompt to install the project's [suggested extensions](https://github.com/GraphiteEditor/Graphite/blob/master/.vscode/extensions.json). They will provide helpful web and Rust tooling. If you use a different IDE, you won't get default configurations for the project out of the box, so please remember to format your code and check CI for errors.
### Checking, linting, and formatting
While developing Rust code: `cargo check`, `cargo clippy`, and `cargo fmt` terminal commands may be run from the root directory. For web code: errors, code quality lints, and formatting issues can be checked using `npm run check` (to view them) and `npm run fix` (to fix them) if run from the `/frontend` directory.
If you don't use VS Code and its format-on-save feature, please remember to format before committing or [set up a `pre-commit` hook](https://githooks.com/) to do that automatically. Disabling VS Code's *Auto Save* files feature is recommended to ensure you actually save (and thus format) file changes. CI will enforce that everything passes these checks before your PR can be merged.
@@ -0,0 +1,16 @@
+++
title = "Starting a task"
template = "book.html"
page_template = "book.html"
[extra]
order = 3 # Chapter number
+++
There are two places to look for beginner-friendly development tasks. Usually, the best option is to select one of the many bite-sized task descriptions marked with a ‼️ reaction in the `#✅code-todo-list` channel of the [Discord server](https://discord.graphite.art). You may also browse the task board for a list of [beginner issues](https://github.com/orgs/GraphiteEditor/projects/1/views/6) to pick from. The Discord option usually has the more approachable tasks, compared to the GitHub issues that often have more variability in complexity.
If you're unsure about which task to pick, feel free to ask in the `#📄development` channel. You can also use that channel to ask for coding help if you anticipate it will be a quick discussion rather than a longer-running conversation that deserves its own thread.
You may right click a `#✅code-todo-list` task and select "Create Thread" to ask questions and discuss your development progress. If your work doesn't correspond to a specific listed task in that channel, you can also create a thread in `#🧵task-help` with a short, descriptive title ending with your issue or PR number.
If you're tackling a GitHub issue, please remember to comment in the issue with a link to your PR once you submit it. This is necessary because we will assign you to the issue after your PR has merged, but GitHub only allows assignments to those who have commented on the issue.
@@ -0,0 +1,23 @@
+++
title = "AI contribution policy"
[extra]
order = 4 # Page number after chapter intro
+++
Many open source projects including Graphite have begun to be spammed with an ever-increasing flood of low-quality PRs written partly or wholly by AI. These harm the project by wasting the time of maintainers and preventing PRs by genuine contributors from receiving timely review. We aim to be reasonable and understanding to contributors who put in the effort, but it has become necessary to set some strict rules against low-effort PRs.
## Acceptable usage
- Non-agent AI tools may **assist** with debugging and tab-completion of single lines of code you would have otherwise written yourself. This does not require disclosure.
- AI chat tools (not agents) may help you **generate** small (sub-40 line) snippets of code that you manually copy and paste, provided that you carefully review every line to ensure it is consistent with how you would have written it yourself. This requires disclosure.
## Unacceptable usage
- AI slop, "vibe-coded", or agent-written PRs are strictly forbidden and may be treated as malicious spam attacks against the project, resulting in a ban.
- PR description text and replies to reviewers must be written by you, not AI. If your English is imperfect, just try your best; it is better than AI babble.
## Required disclosure
- Graphite has **zero-tolerance** for contributing undisclosed AI-generated content.
- A detailed, human-written description must accompany every line of material that you did not personally write using your own brain. It should justify why each line is correct and appropriate. This should be prepared ahead of time and written as self-review comments on the GitHub PR's diff immediately after the PR is opened or new code is pushed.
@@ -0,0 +1,67 @@
+++
title = "Code quality guidelines"
[extra]
order = 2 # Page number after chapter intro
+++
The Graphite project prizes code quality and accessibility to new contributors. Therefore, we ask you please make all efforts to contribute readable, well-documented code according to these best practices.
## Linting
Please ensure Clippy is enabled. This should be set up automatically in VS Code. Avoid committing code with lint warnings so the code review process goes smoothly. You may execute `cargo clippy` anytime to confirm.
## Naming
Please use descriptive variable/function/symbol names and keep abbreviations to a minimum. Prefer spelling out full words most of the time, such as `generate_document_format` instead of `gen_doc_fmt`.
This avoids the mental burden of expanding abbreviations into semantic meaning. Monitors are wide enough to display long variable/function names, so descriptive is better than cryptic.
Totally unambiguous, common shortened forms are acceptable such as "max" for "maximum", "eval" for "evaluate", and "info" for "information".
To avoid wasted effort in code review, it's recommended that you set up a spellcheck plugin, like [this extension](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker) for VS Code. The project uses American English spelling conventions.
## Whole-number floats
Always use the style `42.` instead of `42.0` for whole-number floats to maintain consistency and brevity. For range syntax, either `0.0..42.` or `(0.)..42.` is acceptable.
## Comments
For consistency, please try to write comments (`//`) in *Sentence case* (with a capital first letter) and don't end with a period unless multiple sentences are used in the same comment. For doc comments (`///`), always end your sentences with a period. There should always be one space after the `//` or `///` comment markers, and `/* */` style comments should be avoided.
Avoid including commented-out code in PRs that are open for code review unless you have a compelling reason to keep it around for future reference.
Comments should usually be placed on a separate line above the code they are referring to, not at the end of the same code line.
## Blank lines
Please make a habit of grouping together related lines of code in blocks separated by blank lines. These are like your paragraphs if you were writing a novel — they greatly aid readability and your copy editor would have significant concerns with your writing if they were absent.
If you have dozens of lines comprising a single unbroken block of logic, you are likely not splitting it apart enough to aid readability. Find sensible places to partition the logic and insert blank lines between each. At least 10% of the code you write should ideally be blank lines, otherwise you are likely underutilizing them at the expense of readability.
## Imports
Our imports used to be a mess before we tamed the chaos with a formatting rule that has to be applied manually, since `rustfmt` doesn't support it.
We always combine imports with common paths, but only at the same depth. For example:
```rs
use crate::A::B::C;
use crate::A::B::C::Foo;
use crate::A::B::C::Bar;
// Should be combined into:
use crate::A::B::C::{self, Foo, Bar};
```
But we do not combine imports at mixed path depths. In other words, never put `::` inside `{}`. For example:
```rs
use crate::A::{B::C::Foo, X::Hello};
// Should be separated into:
use crate::A::B::C::Foo;
use crate::A::X::Hello;
```
@@ -0,0 +1,114 @@
+++
title = "Submitting a contribution"
[extra]
order = 3 # Page number after chapter intro
+++
Collaboration is a key part of real-world software engineering. Graphite follows some basic procedures to keep the process smooth and efficient. You will want to familiarize yourself with these guidelines to save yourself and Graphite maintainers time and confusion.
This assumes you understand enough about how Git works to utilize commits, branches, and multiple remotes. If you're new to Git, you will need to learn those topics on your own, but a good starting point is [this portion](https://youtu.be/vUzIeg8frh4?t=237) of the Graphite intro webcast which recommends installing the [Git Graph](https://marketplace.visualstudio.com/items?itemName=mhutchie.git-graph) extension for VS Code to visualize your Git history and branches.
## AI usage
If you are using any form of AI tools in your development workflow, you must read and comply with our [AI contribution policy](../ai-contribution-policy) before submitting your PR.
## Git branch name
Before making your first commit, create a new branch with a name that describes what it's about. Aim for short but sufficiently descriptive. Kebab-case (using hyphens between words) is our usual convention. Don't include a prefix like `feature/` or `fix/` which just adds visual noise. An example like `fix-path-tool-selection-history` is fine, but almost too long.
**Warning: do not open a PR from a branch named `master`.** It makes code review considerably more difficult. Create a new branch if you've already been committing to `master` and open your PR from that correctly-named branch.
After you push your branch to GitHub then open a PR, you won't be able to change its branch name. But please don't close a PR and open a new one just because the branch name isn't optimal. Just keep these tips in mind for the next time.
## Pull request
Once you have gotten your code far enough along that you are confident you'll be able to complete it, open a pull request (PR). You might also do this earlier if a maintainer requests to see your code in order to assist you.
Later on when you are building larger features, a PR should be opened once you have meaningful progress. That way, it can be kept safe on GitHub and other maintainers can check in to see your status so your work is less of a mystery.
**Here's the important part:** when you open a PR, it should be marked as a draft unless it is currently ready for review. The left image shows how to open a new PR as a draft, and the right image shows how to convert an existing PR to a draft.
<p><img src="https://static.graphite.art/content/volunteer/guide/draft-pr.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Screenhots showing GitHub's &quot;Create pull request (arrow) > Create draft pull request&quot; and &quot;Still in progress? Convert to draft&quot; buttons" /></p>
<center><em>Open a new PR as a draft / convert an existing PR to a draft</em></center>
You should mark it as ready for review and ping a maintainer when you believe your code implements the needed functionality and doesn't introduce any new bugs or broken features.
## Title and description
Your PR title will become the commit message of your feature's commit in the project Git history. It should aim to concisely but descriptively summarize what your PR does. We use sentence case and imperative mood ("Fix X bug", "Add Y feature", "Make Z faster").
If you are working on a task from the `#✅code-todo-list` Discord channel, you should right-click the exact message, select "Copy Message Link", and paste that into your PR description.
If you are working on a task with a GitHub issue, please be certain to include that issue number in the description. GitHub requires the format "Closes #123", "Fixes #123", or "Resolves #123". If there are multiple issues, you have to fully repeat this trigger word for each one. If there is no issue, remove the pre-filled "Closes #" description text.
When the PR gets merged, any issue referenced by the trigger word will be automatically closed. That isn't desirable for [tracking issues](https://github.com/GraphiteEditor/Graphite/issues?q=is%3Aissue+is%3Aopen+in%3Atitle+%22tracking+issue%22), so you should instead use "Part of #123" which isn't a trigger word. After the PR merges, please edit the description to change "Part of" to "Closes" so the tracking issue links to the PR without it having gotten closed.
As a bonus, it can be helpful for maintainers if you take a few minutes to write about what you changed and include relevant screenshots or video clips.
If you have concerns about a certain approach you took or if a certain part of your code is as clean as it could be, you can leave comments on lines of your own code from the "Files changed" tab after opening the PR.
## Comment on the issue for assignment after it merges
For any issue referenced in your PR (including tracking issues), we need you to leave a comment on issue. It doesn't matter what you write. You can just say "I opened PR #456" or something to that effect. This is only necessary because we will need to assign that issue to you upon merging your PR, but GitHub only allows assignments to those who have commented.
That way you get credit for your work and we can keep our closed issues cleanly organized. For consistency, a closed issue should have an assignee if it was resolved by a PR. Otherwise, only duplicate or invalid issues should be closed without an assignee.
We don't commonly assign issues while a PR is still in progress, only upon landing the PR. That's because PRs often get abandoned and we don't want an assignment blocking someone else from picking up the work.
## Code review etiquette
It is your responsibility to build the editor, thoroughly test your work, and employ common sense to avoid wasting a maintainer's time in needing to point out obvious flaws. It is not uncommon for inexperienced contributors to request review when their code entirely fails to implement the task at hand, or breaks surrounding functionality in a way that should have been immediately apparent. This doesn't leave a good impression and can frustrate maintainers. It may also be interpreted as AI-generated spam if the mistakes are egregious enough, which will lead to a ban according to our [AI contribution policy](../ai-contribution-policy).
If you don't actually understand what is intended with your feature/fix and why this is meaningful to a user of Graphite, spend time becoming that user and understanding the context. [Learning](/learn) at least the basics of using Graphite is important. Then ask questions in Discord if you're still confused about specific edge cases or the wording of the task.
It is also common for larger tasks to enter a round of review to confirm the direction is correct before you go back and polish the remaining details of the implementation. It's good to be in touch with the team to decide on when is the right time for this kind of preliminary review. It can save you effort reworking problems if you misunderstand the goals, or if the exact details of the requirements were never well-defined and you'll need to iterate on the design together with the team. Don't feel that every part of your PR needs to be 100% finished before requesting feedback, but also be clear so you aren't taking a maintainer away from other work to point out that you are obviously nowhere near done.
## Self-review
Before marking your PR as ready for review, you should do a self-review. That means reading over the diff of all your changes to ensure they are correct, complete, and lacking frivolous changes like unintended whitespace alterations, leftover debugging code, or commented-out lines. Read over it with a fine-toothed comb so maintainers don't have to nitpick as much. It is only fair that your first code reviewer should be yourself, so you catch the obvious flaws first.
Feel free to leave comments on lines of your own code in the diff if you want to communicate concerns or highlight uncertainties to the maintainer. This is also where you must [disclose AI generated lines of code](../ai-contribution-policy) if applicable.
## Passing CI
Upon pushing a commit to your PR's branch, CI will need to build and test your code. PRs from forks will have to wait until a maintainer approves the CI run. If you're uncertain, run `cargo test --all-features` on your machine or ask a maintainer to trigger CI for you.
You also have to pass `cargo fmt` and `cargo clippy` in CI before your PR can be merged. You should run these commands locally before pushing to confirm.
Your goal is for the check called "Editor: Dev & CI / build (pull_request)" to pass with a ✅. If it fails with an ❌, you will need to investigate. If you need access to the build logs, ask a maintainer to provide them. Occasionally, other checks may fail, but you likely won't be responsible for fixing those and they can be ignored.
## Keeping your work up-to-date
Be sure to start your work from the latest commit on the `master` branch by pulling (`git pull`) with `master` checked out when you begin coding.
As time goes on and `master` accumulates new commits, your branch will become outdated. It has to be synced up with `master` before your PR can be merged. Sometimes there will be conflicts that you need to resolve, which you can find learning resources for online. Enabling Git's three-way diff conflict style with `git config --global merge.conflictstyle diff3` can make this process easier.
When your branch can be updated with `master` without conflicts, you can click the "Update branch" button below the CI status. If you click the dropdown button beside it, you can choose instead to update with a rebase. If this can be done without conflicts, this is preferred because it maintains a clean, linear history for your branch.
<p><img src="https://static.graphite.art/content/volunteer/guide/update-branch-with-rebase.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" onload="this.width = this.naturalWidth / 2" alt="Screenhots showing GitHub's &quot;Update with rebase&quot; button" /></p>
Be sure to pull the rebased, or updated-with-a-merge-commit, branch after you or a maintainer updates it (or pushes other commits to it) to ensure you are working on the latest code.
**Please do not** constantly rebase or merge every day while you're waiting for review since it's unhelpful and gets annoying. A reviewer will do that for you if there are no conflicts. But if there are conflicts, you *will* need to resolve them and push the updated code before review.
## Review process
Assuming you have done what's explained above, a maintainer will aim to review your PR within a few days if possible. Feel free to send reminders because PRs can get overlooked.
As a rule of thumb, at this stage you are about 50% done with your work. The other 50% of your time will be spent responding to feedback and making (sometimes significant) changes.
There are two parts to the review process, QA and code review, which occur separately:
- Quality assurance (QA): A build of your code will be opened and tested to ensure it implements the requested functionality and doesn't introduce regressions. This is not a substitute for your own testing, but it is a necessary line of defense against overlooked issues. This is usually performed by Keavon, the founder and product designer, whose eye for detail keeps the app polished and consistent. Maintainers (and only maintainers) have the ability to invoke CI by commenting "!build" on your PR which will produce a build link. That is a unique link hosting a build of your PR's current code.
- Code review: The code will be checked for flawed approaches, pitfalls, confusing logic, [style guide](../code-quality-guidelines) adherence, sufficient comments and tests, and general quality. A review may be left through GitHub or your PR may have commits added to it. Feel free to read the diffs of those commits to understand what was changed so you can learn from that feedback. Direct commits are often faster than leaving dozens of comments. These can range from nitpicks to larger improvements. Our process is to collaborate on PRs as a team to write the best code possible, meaning your PR won't always be exclusively written by you.
When changes are requested, the maintainer will usually mark the PR as a draft again while awaiting your updates. **It is your responsibility to mark it as ready for review** again once you have addressed the feedback.
- If a PR is a draft, the ball is in your court to move it forward.
- If it's marked as ready for review, it means there is nothing more for you to do until the maintainer has time to review it. (You're encouraged to work on other PRs while waiting.)
After any number of back-and-forth cycles, a maintainer (usually Keavon who often gives the final say) will merge your PR. All your commits will be squashed into a single new commit on the `master` branch. This keeps the Git history linear and easy to follow.
Congratulations on landing your successful contribution! Post a request in `#📄development` on Discord to be assigned the *"Code Contributor"* role.
@@ -0,0 +1,218 @@
+++
title = "Student projects"
template = "book.html"
page_template = "book.html"
[extra]
order = 4 # Chapter number
+++
Graphite offers a number of opportunities for students to contribute by building a self-contained project as part of a structured format. These projects are designed to be completed over several months and are ideal for Google Summer of Code or similar internship programs, solo or group university capstone projects, and other arrangements. Each project has a distinct focus and is a great way to make a meaningful contribution to open source over the length of the program while receiving mentorship and guidance from the Graphite team.
Student projects require adherence to a set schedule with regular check-ins, milestones, and evaluations. The structured setting is designed to provide a supportive environment for students to learn and grow as developers while gaining real-world industry experience from collaborating on a sizable software product and remaining accountable to stakeholders. It's our goal to make sure you succeed!
Use this [contributor guide](..) to start out with the code. Then when you're ready, reach out through [Discord](https://discord.graphite.art) and use the `#🎓student-projects` channel to discuss and work towards proposing a project with the Graphite core team.
## AI contribution policy
Be sure to familiarize yourself with our [AI contribution policy](../starting-a-task/ai-contribution-policy) before getting involved with the Graphite code base. Proposals also must not be written by AI or else they will be rejected.
## Google Summer of Code
GSoC is a program offering students a [stipend](https://developers.google.com/open-source/gsoc/help/student-stipends) for successful completion of an internship-style experience with an open source organization. Read about [how it works](https://summerofcode.withgoogle.com/how-it-works/).
Graphite participated in GSoC [2024](https://summerofcode.withgoogle.com/programs/2024/organizations/graphite) and [2025](https://summerofcode.withgoogle.com/programs/2025/organizations/graphite) and we anticipate doing so again in [2026](https://developers.google.com/open-source/gsoc/timeline) if our organization is accepted back. We accept year-round contributions; getting involved early is a great way to have a head start and stand out in your application in the upcoming program.
### Writing a proposal
Writing a good proposal is an important step that demonstrates your understanding of the project and your ability to think ahead and execute it. A well-defined plan will set you up for success throughout the rest of the program.
<details>
<summary>For proposal writing guidelines and requirements: click here</summary>
You are encouraged to reference the project idea list below to find several potential projects suited to your experience, interest, and choice of scope. Then, you must reach out to a [core team member](/about#core-team) through Discord to discuss your plan in detail before writing a proposal. This will help you understand the project's scope and requirements and develop a detailed timeline for your expected summer-long work schedule. Importantly, it will also help us understand your background and capabilities to offer you feedback and suggestions for the best outcome in the competitive applicant selection process.
When it comes to writing the proposal, which you will submit to the GSoC application website, we offer some guidelines below:
- **Proposal structure:** Please consult the [Blender GSoC application template](https://developer.blender.org/docs/programs/gsoc/application_template/) as reference for our desired format. For project ideas already listed below, omit the "Benefits" section. Remember: don't waste your—and our—time restating information that we already know, like background info about Graphite or our tech stack; we just want to hear your thoughts and plans about what you uniquely bring to the table and how you'll execute the project. Proposals should be utilitarian, not formal, while also demonstrating your professional communication skills. Using an LLM to write your proposal won't be to your advantage.
- **Experience:** We're especially interested in your background and work experience, so attaching a résumé or CV is an optional but recommended way to help us understand your capabilities. If able, please also include links to past open source contributions or personal projects in the bio section. Our goal is to provide an environment for you to learn and grow as a productive software engineer and team collaborator, not to help you learn the basics of coding, so any included work examples will help us understand your potential as a self-motivated contributor to the open source community.
- **Work timeline:** Your goal is to write a proposal that inspires confidence in your ability to successfully complete the project, which means understanding in detail what's involved at a technical level and how you plan to tackle it. A detailed work timeline is the most important written part of your proposal. It should be broken into weekly milestones with a couple sentences of technical detail. The summary in the project idea list below doesn't give enough information to develop a timeline, so you'll need to discuss this with the core team on Discord.
- **Prior PRs:** The largest factor in our selection decision will be the quality and extent of your prior contributions to Graphite made during the proposal formulation period (or before, if applicable). Include a link to `https://github.com/GraphiteEditor/Graphite/commits?author=YOUR_GITHUB_USERNAME` in your proposal and feel free to write up a summary of what you've contributed and learned from the process. You may also keep contributing during the month after applications close, before we've finalized our selections, for those additional PRs to be considered.
</details>
## Project idea list
Projects listed below vary considerably in their required skills and technical background. Some are very research-heavy and are only suited for students with years of self-motivated learning and project development in adjacent topics. Others have a more general focus and are approachable to a wider range of students. Please pay close attention to the "Needed Skills" and "Difficulty" indicators so you don't waste your opportunity applying to a project we don't think you're a good fit for.
<!--
- System for nodes displaying gizmos to update their parameters
- Category of tools for repeating, mirroring, patterning, and manipulating objects ("recipes")
- Text improvements (formatting spans, flows between text areas, text-on-path)
- Feature-complete SVG import and rendering support
-->
### Graphene language bidirectional type inference
*Graphene needs to implement a more powerful type system so a generic type may be inferred based on surrounding context of the type's usage constraints.*
- **Possible Mentors:** [Dennis](https://github.com/truedoctor)
- **Needed Skills:** Rust, type theory, programming languages theory, past experience implementing such a system
- **Project Size:** Large *(GSoC: 350 hours)*
- **Difficulty:** Hard
- **Expected Outcomes:** A complete implementation to upgrade the current limited type inference system. The new system should work like Rust's, where variables of unknown types can be given a type satisfying the later usages of the variable.
Consider a node with a generic input parameter which is connected to a node supplying a concrete type. As long as the type is one that satisfies the constraints of the generic parameter, this is valid. The current system checks for this single-directional constraint. But many cases arise where this is insufficient. For example, if the generic parameter is used in multiple places with different constraints, the system needs to be able to infer a type that satisfies all of those constraints.
<details>
<summary>For additional technical details: click here</summary>
Read more about [HM type inference](https://en.wikipedia.org/wiki/Hindley%E2%80%93Milner_type_system), a powerful (but potentially more complex than necessary) model. See also the [GitHub issue](https://github.com/GraphiteEditor/Graphite/issues/2350) describing this, where you can ask questions if needed. This is an advanced topic and only suitable for individuals who have already implemented a similar system in a programming language or compiler project before.
</details>
### Node equivalence rewriting
*A sequence of nodes may perform operations on data that can be expressed using fewer equivalent nodes, and users may often wish to perform such simplifications.*
- **Possible Mentors:** [Dennis](https://github.com/truedoctor)
- **Needed Skills:** Rust, graph theory, algorithm design
- **Project Size:** Large *(GSoC: 350 hours)*
- **Difficulty:** Hard
- **Expected Outcomes:** A system for classifying and tracking data transformations symbolically within the DAG of the node graph. A system for applying rewrite rules to selected portions of the graph to produce an equivalent graph with fewer nodes. Integration with the editor to allow users to apply simplifications to selected nodes, especially to transforms and geometry.
Oftentimes, node graphs contain redundant steps that collectively perform a simpler operation. For example, two Transform nodes may produce the same result as a single Transform node with the combined transformation. Or a node that generates a star shape, then a Path node that applies a differential modification to its geometry, may be equivalent to a single Path node that produces the same geometry in one step. This project is about architecting and integrating a system for tracking classes of data transformations, like transforms or geometric modifications or appearance changes, and allowing the user to select the redundant nodes to collapse or "bake" them into a simpler graph with identical output. This is sort of like selecting the terms of a math expression and applying algebraic simplification rules to reduce it to its simplified form.
<details>
<summary>For additional technical details: click here</summary>
This is best for someone with an interest towards graph theory and compiler optimization topics like [E-graphs](https://en.wikipedia.org/wiki/E-graph). Additional detail is provided in the [GitHub issue](https://github.com/GraphiteEditor/Graphite/issues/2021) including some introductory explanation about E-graphs from a Rust crate that implements them, [egg](https://egraphs-good.github.io/).
</details>
### Machine learning architecture
*AI/ML image/vision models for content editing will need to run in Graphite's node graph with a Rust-centric, modular, portable, deployable, scalable environment.*
- **Possible Mentors:** [Oliver](https://github.com/otdavies)
- **Needed Skills:** Machine learning (and potentially: Rust, Python, ONNX, Burn)
- **Project Size:** Large *(GSoC: 350 hours)*
- **Difficulty:** Hard
- **Expected Outcomes:** Specifics will vary by proposal. In general, a useful end-to-end integration of at least one image model into Graphite's node graph which can run both locally and deployed to a hosting provider server.
AI/ML is filling a rapidly growing role in the industry as a tool in some creative processes. Graphite's procedural node-based workflow is uniquely suited to leveraging the power and flexibility of AI nodes.
[Segment Anything 2](https://ai.meta.com/research/sam2/) (object segmentation) and [Depth Anything 3](https://github.com/ByteDance-Seed/Depth-Anything-3) (depth estimation) are currently the models we are most [interested in integrating](https://github.com/GraphiteEditor/Graphite/issues/1694). The challenge is settling on an architecture and tech stack which is well suited for Graphite's requirements.
<details>
<summary>For additional technical details: click here</summary>
The approach should be extensible to future models. It needs to run fast and natively on the assorted hardware of local user machines with hardware acceleration. It should be a one-click installation process for users to download and run models without requiring dependencies or environment setup. Ideally, it should allow the more lightweight models to run locally in browsers with WebGPU. It needs to also be deployable to servers in a scalable, cost-viable manner that reuses most of the same code that runs locally. Runtime overhead, cold start times, and memory usage should be minimized for quick, frequent switching between models in a node graph pipeline. The tech stack also needs to be permissively licensed and, as much as possible, Rust-centric so it doesn't add complexity to our Wasm and desktop build processes.
To meet most of these criteria, our current thinking is to distribute and run our models using the [ONNX](https://onnx.ai/) format. This would integrate ONNX runtimes for WebGPU, native, and GPU cloud providers. One challenge is that many of the best-performing models are not packaged in ONNX format, but this approach also allows for direct implementation of model architectures in Rust.
[Burn](https://burn.dev/) is Rust's most promising and advanced machine learning framework, and in addition to Rust model implementations, it also [supports](https://github.com/tracel-ai/burn-onnx) ONNX model loading for conversion into its native format.
Another potential direction is to find a portable, modular, lightweight approach for bundling existing Python-based models. It would need to work across simple and complex models with different architectures. License compliance, if GPL code is involved, would be a consideration.
Based on the experience and insight brought to the table by the student, the nature of the project should be defined through preliminary discussions with the mentors and codified in the proposal. Machine learning and MLOps are fields that Graphite's team lack deep expertise in, so we are looking for a knowledgable student who can bring forth a well-researched and well-architected proposal and then execute on it.
</details>
### Generalized graphical data rendering representation
*Rendering graphical content like colors, gradients, patterns, and whole other layers needs to be possible in a more flexible way that can target the fills and strokes of vector shapes.*
- **Possible Mentors:** [Keavon](https://github.com/keavon)
- **Needed Skills:** Rust, SVG
- **Project Size:** Medium or Large *(GSoC: 175 or 350 hours)*
- **Difficulty:** Medium
- **Expected Outcomes:** Improved SVG and Vello renderer implementations that can handle a wider variety of paint types and effects. Support for every combination of paint type with its application to fills, strokes, and full-canvas drawing. Inclusion of the specified paint source types in the graphical data model and appropriate nodes for generating and handling such data.
Presently, Graphite has a limited methodology for defining what gets painted when rendering vector shape fills and strokes. Solid colors and spatially positioned gradients are supported for fills, but only solid colors for strokes. Also, gradients cannot be painted across the entire canvas, and patterns do not exist at all yet. This project involves refactoring the renderer and data model to support a more generalized representation of paint sources that can be applied to fills, strokes, and entire layers. It deprecates the current solid/gradient/none selection for fills and solid/none selection for strokes in favor supporting anything that could be painted as a layer.
<details>
<summary>For additional technical details: click here</summary>
An extended description and a list of child issues is available in the [GitHub issue](https://github.com/GraphiteEditor/Graphite/issues/2779). A large-sized project would likely include support for the polyfilled gradient types described in the sub-issues of [this task](https://github.com/GraphiteEditor/Graphite/issues/2304).
</details>
<!-- ### Advanced text layout and typography
*This is a newly added project pending a full written overview. Come ask on Discord for details.*
- [See the GitHub issue.](https://github.com/GraphiteEditor/Graphite/issues/1105)
### Brush engine
*This is a newly added project pending a full written overview. Come ask on Discord for details.*
- [See the GitHub issue.](https://github.com/GraphiteEditor/Graphite/issues/1297)
### Advanced color management
*This is a newly added project pending a full written overview. Come ask on Discord for details.*
- Add support for HDR/WCG and/or CMYK and alternate color spaces/models
- Requires an experienced understanding of color science
### SVG with raster effects
*This is a newly added project pending a full written overview. Come ask on Discord for details.*
- The SVG spec supports a number of filters and other raster effects, and we currently only implement a small subset.
- Add support for the rest of the SVG spec, including filters, masks, and other raster effects.
- Allow roundtrip import and export of SVG files with these features.
- Import, render (through SVG and Vello), and export of [filters like these](https://codepen.io/miXTim/pen/ZErggMQ).
### Snapping system overhaul
*This is a newly added project pending a full written overview. Come ask on Discord for details.*
- [See the GitHub issue.](https://github.com/GraphiteEditor/Graphite/issues/2352)
### Tooling polishing and gizmo additions
*This is a newly added project pending a full written overview. Come ask on Discord for details.* -->
### Marquee selection masking
*Graphite's raster editing features requires the implementation of Select mode, where users can draw a mask which becomes a marquee (marching ants) selection.*
- **Possible Mentors:** [Keavon](/about#keavon)
- **Needed Skills:** Rust, computer graphics
- **Project Size:** Large *(GSoC: 350 hours)*
- **Difficulty:** Medium
- **Expected Outcomes:** Complete implementation of Mask mode and its marquee selection. Marching ants visualization shader effect. Integration of selection mask with the node graph and raster editing tools. Useful raster editing workflow.
A central part of the workflow in raster image editors is the selection of portions of the image to constrain manipulations just to the masked areas. Tools such as the circular and rectangular marquee, lasso, and magic wand are used to create masks. Instead of using dedicated tools, Graphite's design reuses the existing vector and raster drawing tools (like Rectangle, Ellipse, Pen, and Fill) to create masks in a dedicated Mask mode. Returning from Mask mode reveals the marching ants selection that constrains further editing operations.
This is a key feature in Graphite's evolution to a fully-featured raster editor.
### Testing and performance instrumentation
*Graphite has many areas that could benefit from better automated testing for bugs and performance regressions.*
- **Possible Mentors:** [Dennis](/about#dennis)
- **Needed Skills:** Rust, unit testing
- **Project Size:** Small *(GSoC: 90 hours)* or larger if proposed
- **Difficulty:** Easy
- **Expected Outcomes:** Specific focus and scope may vary by the student's interests and proposal. In general, a significant increase in the coverage of tests in useful code areas (such as document loading, tool manipulation, and rendering) and attention towards systems which measure performance metrics and identify bottlenecks and regressions.
Graphite could benefit from better testing coverage in a number of areas, especially end-to-end testing in the tool, document, and node graph systems. This project is about identifying and addressing areas that are lacking and most vulnerable to suffering from regressions. The student will be responsible for identifying areas that could benefit from better testing.
### Your own idea
*If you have an idea for a project that you think would be a good fit, we'd love to hear it!*
- **Possible Mentors:** Varies
- **Needed Skills:** Varies
- **Project Size:** Varies
- **Difficulty:** Varies
- **Expected Outcomes:** Stated in your proposal.
If none of the projects above suit your interests or experience, we are very open to discussing your own project ideas that could benefit Graphite. You may consult our [task board](https://github.com/orgs/GraphiteEditor/projects/1/views/1) and [roadmap](/features#roadmap) to get a feel for what our current priorities are.
As is the case with all projects, please discuss this with us on Discord to flesh out your idea. Unsolicited proposals that have not been discussed with us will almost certainly be rejected.
@@ -0,0 +1,119 @@
+++
title = "Completed projects"
[extra]
order = 2 # Page number after chapter intro
+++
We keep an archive of our successful student projects from past years to help prospective applicants get a better feel for the types and scope of projects we can support.
## 2025
### GPU-accelerated raster operations
*Raster operations are limited to slow CPU-based fallbacks while GPU shader implementations require further infrastructure engineering.*
Affiliation: GSoC 2025
Duration: 3 months
Student: Firestar99
- [Program project listing](https://summerofcode.withgoogle.com/organizations/graphite/projects/details/TfdLAuN4)
- [Report and weekly updates](https://github.com/GraphiteEditor/Graphite/discussions/2658)
**Outcomes:** Restructuring of dependencies within the node and data type definitions to allow for `#[no_std]` in the implementations of shader-driven raster nodes. Introduction of a compile-time pipeline for loading and compiling CPU node implementations to shader code using [Rust GPU](https://github.com/Rust-GPU/rust-gpu). Node definition macro changes to declare per-pixel color adjustment nodes as fragment shaders. Upstream improvements to Rust GPU and its build tool, [Cargo GPU](https://github.com/Rust-GPU/cargo-gpu), to support Graphite's use cases while avoiding a need for the rest of the Graphite project adopt the nightly Rust toolchain.
**Background:** Graphite's node graph engine executes and renders graphics by means of defining artwork as procedural node graph programs within the purpose-built Graphene language. Each graphics operation is a node with, at minimum, a CPU implementation which supports compilation along with the rest of the graph into an executable Graphene program for rendering to the screen. A major goal is to share that same CPU implementation for the GPU version that compiles to a GPU shader in order to maintain identical algorithms between versions and avoid the maintenance burden of separate code paths. However, GPU architectures enforce challenging constraints which leaves this goal as yet unrealized. The project must tackle the engineering challenges of setting up a basic shader compilation system and integrate GPU versions of nodes into the Graphite editor and its render pipeline.
### 3 additional projects (summaries coming soon)
See the [program listing](https://summerofcode.withgoogle.com/programs/2025/organizations/graphite) for more details until the other three GSoC 2025 project summaries are added here.
## 2024
### Interactive node graph auto-layout
*Graphite's graph UI needs a system to automatically arrange layers and nodes given incremental changes to the graph contents.*
Affiliation: GSoC 2024
Duration: 3 months
Student: Adam Gerhant
- [Program project listing](https://summerofcode.withgoogle.com/programs/2024/projects/gvbBoCpT)
- [Report and weekly updates](https://github.com/GraphiteEditor/Graphite/discussions/1769)
**Outcomes:** A system that manages the placement of nodes based on a set of layout constraint rules and incremental updates to the graph topology. It should run efficiently, even with large graphs. It should be robust enough to handle a variety of graph topologies and user interactions, producing organized, useful, and stable layouts.
**Background:** The Graphite concept is built around a node graph representation of layer stacks, while tools automatically generate and manipulate nodes. When a layer or node is inserted, deleted, moved, or referenced, the graph needs to be reorganized to maintain a clear and useful layout. Users can also interactively expand and collapse groups of nodes which occupies or frees up graph real estate.
Unlike other node editors that are centered around manual graph editing, where users are fully in charge of node placements within one large node network, Graphite's node UI is more oriented towards automatic layout management and viewing just parts of the graph at one time. This means the shown graph topology is constantly changing and the layout system needs to cooperatively organize the graph in concert with user actions.
While general graph layout algorithms are complex and struggle to produce good results in other node editors, Graphite's graph topology is more constrained and predictable, which makes it possible to design a layout system that can produce good results. Nodes tend to be organized into rows, and layers into columns. This turns the problem into more of a constraint-based, axis-aligned packing problem.
### Rendering performance infrastructure improvements
*Graphite performance is bottlenecked by limitations in the new node graph rendering architecture that needs improvements.*
Affiliation: GSoC 2024
Duration: 4 months
Student: Dennis Kobert
- [Program project listing](https://summerofcode.withgoogle.com/programs/2024/projects/v5z2Psnc)
- [Report and weekly updates](https://github.com/GraphiteEditor/Graphite/discussions/1773)
**Outcomes:** A holistic, metrics-driven focus on fixing the many unoptimized areas of Graphite's node graph compilation, execution, and rendering systems. Integration of Vello as an integrated rendering backend. A significant improvement in the performance of the editor, especially in the node graph, and a more stable and predictable performance profile. Benchmarking and profiling tools to measure and visualize performance improvements and regressions.
**Background:** Graphite's node graph system is the backbone of the editor, but it has many performance problems that need to be addressed because the system is relatively immature and performance-impacting shortcuts were taken during its initial development. This project is all about making the node graph system more robust and optimized, which will have a direct impact on the user experience and the editor's overall performance. By the end of the project, the editor should finally feel usable in the majority of user workflows. Vello should be enabled as an alternate render engine that will fully replace the existing SVG-based one in the future, once browser support arrives across major platforms.
### Raw photograph decoding in Rust
*For Graphite to support editing photos from professional digital cameras, it needs a raw decoding/processing library.*
Affiliation: GSoC 2024
Duration: 5 months
Student: Elbert Ronnie
- [Program project listing](https://summerofcode.withgoogle.com/programs/2024/projects/2uiwOfz8)
- [Report and weekly updates](https://github.com/GraphiteEditor/Graphite/discussions/1771)
- [Rawkit library](https://crates.io/crates/rawkit)
**Outcomes:** A Rust library that implements raw photo decoding functionality to native Rust. A clean, well-structured codebase and API. At a minimum, demonstrate the successful end-to-end decoding, debayering, and color space handling of Sony ARW format photos in Graphite. Publish the library to crates.io.
**Background:** For Graphite to work as a photo editing app, it needs to import raw photos. These contain compressed sensor imagery and metadata in a variety of formats. Sony ARW is the first target and additional camera brands are stretch goals. Graphite needs a library written in pure Rust with a suitable (non-GPL) license, which does not currently exist in the ecosystem, so we need to create one ourselves.
## 2023
### Bezier-rs library
*Graphite's vector editing features require the implementation of Bezier curve and path manipulation computational geometry algorithms.*
Affiliation: University of Waterloo, Ontario, Canada
Duration: 9 months
Students: Hannah Li, Rob Nadal, Thomas Cheng, Linda Zheng, Jackie Chen
- [Bezier-rs library](https://crates.io/crates/bezier-rs)
- [Interactive web demo](https://keavon.github.io/Bezier-rs/)
**Outcomes:** The student group designed an API for representing and manipulating Bezier curves and paths as a standalone Rust library which was published to crates.io. It now serves as the underlying vector data format used in Graphite, and acts as a testbed for new computational geometry algorithms. The team also built an interactive web demo catalog to showcase many of the algorithms, which are also handily embedded in the library's [documentation](https://docs.rs/bezier-rs/latest/bezier_rs/).
## 2022
### Backend layout system
*Graphite's UI needs a system to define and manage layouts for widgets from the backend.*
Affiliation: California Polytechnic State University, San Luis Obispo, USA
Duration: 3 months
Student: Max Fisher
**Outcomes:** The student designed and implemented a new system across the editor's frontend and backend which made it possible to define and manage layouts for widgets from the backend and receive input data from those widgets. Previously, all layouts were statically defined in the frontend and extensive plumbing was required to pass data back and forth.
### Path boolean operations
*Graphite's vector editing features require the implementation of boolean operations on paths, such as union, intersection, and difference.*
Affiliation: California Polytechnic State University, San Luis Obispo, USA
Duration: 3 months
Student: Caleb Dennis
**Outcomes:** The student devised and prototyped algorithms for performing boolean operations on paths, such as union, intersection, and difference. These were used as a stopgap during 2022 and 2023 to provide users with a rudimentary boolean operation feature set.