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,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>