\9 \9 \9 \9 \9 \9 \9 \9 ");width:.75em;height:.75em;margin-bottom:-.1em;margin-left:.25em;display:inline-block}@media screen and (width<=1200px){body>.page header nav .row .left,body>.page header nav .row .right{--height:50px;--button-padding:16px;--nav-font-size:24px;gap:30px}}@media screen and (width<=1000px){body>.page header nav .row .left,body>.page header nav .row .right{--button-padding:14px;--nav-font-size:20px;gap:30px}}@media print,screen and (width<=900px){body>.page header nav .row .left,body>.page header nav .row .right{--height:40px;--button-padding:13px;--nav-font-size:18px;gap:20px}}@media print,screen and (width<=780px){body>.page header nav .row .left,body>.page header nav .row .right{--button-padding:12px;--nav-font-size:16px;gap:20px}}@media screen and (width<=680px){body>.page header nav .row .left,body>.page header nav .row .right{--height:30px;--button-padding:8px;--nav-font-size:14px;gap:16px}}@media screen and (width<=580px){body>.page header nav .row .left,body>.page header nav .row .right{--height:24px;--nav-font-size:13px;gap:12px}}@media screen and (width<=520px){body>.page header nav .row .left,body>.page header nav .row .right{--height:22px;--button-padding:6px;--nav-font-size:12px;gap:10px}}@media screen and (width<=460px){body>.page header nav .row .left,body>.page header nav .row .right{--height:20px;--button-padding:4px;--nav-font-size:11px;gap:8px}}@media screen and (width<=420px){body>.page header nav .row .left,body>.page header nav .row .right{--nav-font-size:10px;gap:6px}}@media screen and (width<=380px){body>.page header nav .row .left,body>.page header nav .row .right{--nav-font-size:9px;gap:6px}}@media screen and (width<=350px){body>.page header nav .row .left,body>.page header nav .row .right{--nav-font-size:8px;gap:6px}}body>.page header .ripple{fill:#fff;stroke:currentColor;--ripple-height:16px;height:var(--ripple-height);margin-top:calc(-1*var(--ripple-height) + var(--border-thickness));margin-bottom:calc(-1*var(--border-thickness));stroke-width:var(--border-thickness);background:0 0;display:block}body>.page header .ripple:before,body>.page header .ripple:after{content:none}body>.page header hr{background:0 0}@media screen and (width<=1400px){body>.page header .ripple{width:calc(100% + var(--page-edge-padding)*2);margin-left:calc(-1*var(--page-edge-padding));margin-right:calc(-1*var(--page-edge-padding))}body>.page header hr{display:none}}body>.page main{padding:calc(120*var(--variable-px))var(--page-edge-padding)}body>.page main>section{max-width:var(--max-width);z-index:1;margin-left:auto;margin-right:auto;position:relative}body>.page main>section~section{margin-top:calc(120*var(--variable-px))}body>.page main>section p img{max-width:100%}body>.page main>section pre{box-sizing:border-box;overflow:auto}body>.page main>section details{width:100%}body>.page main>section details summary{padding-left:18px;font-weight:800;display:inline-block;position:relative}body>.page main>section details summary:before{content:"";background:url("data:image/svg+xml;utf8,\9 \9 \9 \9 \9 \9 \9 \9 \9 \9 \9 \9 \9 \9 ");width:10px;height:10px;margin:auto;position:absolute;top:0;bottom:0;left:0}body>.page main>section details summary+*{margin-top:20px}body>.page main>section details[open] summary:before{transform:rotate(90deg)}body>.page footer{color:var(--color-walnut);flex-direction:column;align-items:center;padding:0 40px 40px;display:flex}@media screen and (width<=1400px){body>.page footer hr{width:100%}body>.page footer hr:before,body>.page footer hr:after{width:calc(var(--page-edge-padding) + 40px);height:var(--border-thickness);background:currentColor;border:none}}body>.page footer nav{flex-wrap:wrap;justify-content:center;gap:8px 40px;margin-top:40px;display:flex}body>.page footer nav a{color:var(--color-walnut)}@media screen and (width<=900px){body>.page footer nav{max-width:500px}}@media screen and (width<=780px){body>.page footer nav{max-width:440px}}@media screen and (width<=400px){body>.page footer nav{gap:6px 20px}}body>.page footer span{text-align:center;margin-top:40px}:is(h1,h2,h3,h4,article>:first-child,details>summary)~:is(p,ul,ol,ol li p,img,a:has(>img:only-child)),:is(h1,h2,h3,h4,article>:first-child)~:is(ul,ol) li p+img,:is(h1,h2,h3,h4,p)~.feature-icons,p~:is(h1,h2,h3,h4,details summary,blockquote,.image-comparison,.video-background,.youtube-embed),.youtube-embed+:is(p,.link,.button),p+p>.button,p+:is(.link,section,details),table td p~p,img+.link,article{margin-top:20px}*{min-width:0;min-height:0}h1{font-feature-settings:"lnum";margin:0;font-family:Bona Nova,Palatino,serif;font-size:2.66667rem;font-weight:700;line-height:1.25;display:inline-block}h1~h2{margin-top:40px}h1~hr{margin-top:40px;margin-bottom:20px}h2,h3,h4,h5,h6{margin:0;font-family:Inter Variable,sans-serif;font-weight:800;line-height:1.5;display:inline-block}h2{font-size:1.77778rem;font-weight:700}h3{font-size:1.33333rem}h4,h5,h6{font-size:1rem}p{text-align:justify;text-justify:inter-character;-webkit-hyphens:auto;hyphens:auto;margin:0}p code{text-justify:auto}h1~img,h1~iframe,h1~a>img:only-child,h2~img,h2~iframe,h2~a>img:only-child,h3~img,h3~iframe,h3~a>img:only-child,h4~img,h4~iframe,h4~a>img:only-child,h5~img,h5~iframe,h5~a>img:only-child,h6~img,h6~iframe,h6~a>img:only-child,p~img,p~iframe,p~a>img:only-child{width:100%;height:auto}a{color:var(--color-crimson)}img{vertical-align:top}table{width:calc(100% + 40px);margin:20px -20px}table th,table td{vertical-align:top;border:20px solid #0000;margin:0;padding:0}table th:not(:first-child) img,table td:not(:first-child) img{max-width:100%}table th:empty{border:none}:is(h1,h2,h3,h4,h5,h6)+table{margin-top:0}ul,ol{margin:0}ul+p,ol+p{margin-top:0}ul li,ol li{margin-top:.5em}hr{color:unset}code{color:#000;background:var(--color-fog);overflow-wrap:anywhere;-webkit-hyphens:none;hyphens:none;padding:0 4px}a code{color:var(--color-crimson)}kbd{outline:calc(var(--border-thickness)/2)solid var(--color-navy);color:inherit;margin:0 4px;padding:0 8px;font-family:inherit}summary{cursor:pointer}hr{overflow:visible}hr,.ripple{width:calc(100% - 64px);height:var(--border-thickness);background:currentColor;border:none;margin:0 32px;position:relative}hr:before,.ripple:before{border-width:0 0 var(--border-thickness)40px;left:-40px}hr:after,.ripple:after{border-width:0 40px var(--border-thickness)0;right:-40px}hr:before,hr:after,.ripple:before,.ripple:after{content:"";border-style:solid;border-color:#0000 #0000 currentColor;width:0;height:0;display:block;position:absolute}.block{flex-direction:column;align-items:flex-start;width:100%;display:flex}.block.centered{align-items:center}:not(.diptych,.triptych)>.block+.block{margin-top:calc(120*var(--variable-px))}.link{font-size:var(--font-size-link);color:var(--color-crimson);white-space:nowrap;font-weight:800;text-decoration:none;display:inline-block}.link:not(.not-uppercase){text-transform:uppercase}.button{border:var(--border-thickness)solid currentColor;min-height:calc(var(--font-size-link)*2);font-size:var(--font-size-link);padding:0 var(--font-size-link);box-sizing:border-box;text-align:left;color:var(--color-crimson);font-weight:800;text-decoration:none;display:inline-block}.button:before{content:"";line-height:calc(var(--font-size-link)*2 - 2*var(--border-thickness))}.button img{height:calc(var(--font-size-link)*1.5);margin-right:calc(var(--font-size-link)/2)}.button img,.button span{vertical-align:middle}.arrow:after{content:" »";font-family:Inter Variable,sans-serif}.video-background{font-size:0;position:relative}.video-background video{max-width:min(100%,1280px)}.video-background:after{content:"";pointer-events:none;border:2px solid #fff;position:absolute;inset:0}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/fonts/files/inter-cyrillic-ext-opsz-normal.woff2)format("woff2-variations");unicode-range:U+460-52F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/fonts/files/inter-cyrillic-opsz-normal.woff2)format("woff2-variations");unicode-range:U+301,U+400-45F,U+490-491,U+4B0-4B1,U+2116}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/fonts/files/inter-greek-ext-opsz-normal.woff2)format("woff2-variations");unicode-range:U+1F??}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/fonts/files/inter-greek-opsz-normal.woff2)format("woff2-variations");unicode-range:U+370-377,U+37A-37F,U+384-38A,U+38C,U+38E-3A1,U+3A3-3FF}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/fonts/files/inter-vietnamese-opsz-normal.woff2)format("woff2-variations");unicode-range:U+102-103,U+110-111,U+128-129,U+168-169,U+1A0-1A1,U+1AF-1B0,U+300-301,U+303-304,U+308-309,U+323,U+329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/fonts/files/inter-latin-ext-opsz-normal.woff2)format("woff2-variations");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/fonts/files/inter-latin-opsz-normal.woff2)format("woff2-variations");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Inter Variable;font-style:italic;font-display:swap;font-weight:100 900;src:url(/fonts/files/inter-cyrillic-ext-opsz-italic.woff2)format("woff2-variations");unicode-range:U+460-52F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Inter Variable;font-style:italic;font-display:swap;font-weight:100 900;src:url(/fonts/files/inter-cyrillic-opsz-italic.woff2)format("woff2-variations");unicode-range:U+301,U+400-45F,U+490-491,U+4B0-4B1,U+2116}@font-face{font-family:Inter Variable;font-style:italic;font-display:swap;font-weight:100 900;src:url(/fonts/files/inter-greek-ext-opsz-italic.woff2)format("woff2-variations");unicode-range:U+1F??}@font-face{font-family:Inter Variable;font-style:italic;font-display:swap;font-weight:100 900;src:url(/fonts/files/inter-greek-opsz-italic.woff2)format("woff2-variations");unicode-range:U+370-377,U+37A-37F,U+384-38A,U+38C,U+38E-3A1,U+3A3-3FF}@font-face{font-family:Inter Variable;font-style:italic;font-display:swap;font-weight:100 900;src:url(/fonts/files/inter-vietnamese-opsz-italic.woff2)format("woff2-variations");unicode-range:U+102-103,U+110-111,U+128-129,U+168-169,U+1A0-1A1,U+1AF-1B0,U+300-301,U+303-304,U+308-309,U+323,U+329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Inter Variable;font-style:italic;font-display:swap;font-weight:100 900;src:url(/fonts/files/inter-latin-ext-opsz-italic.woff2)format("woff2-variations");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Inter Variable;font-style:italic;font-display:swap;font-weight:100 900;src:url(/fonts/files/inter-latin-opsz-italic.woff2)format("woff2-variations");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Bona Nova;font-style:normal;font-display:swap;font-weight:700;src:url(/fonts/files/bona-nova-cyrillic-ext-700-normal.woff2)format("woff2"),url(/fonts/files/bona-nova-cyrillic-ext-700-normal.woff)format("woff");unicode-range:U+460-52F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Bona Nova;font-style:normal;font-display:swap;font-weight:700;src:url(/fonts/files/bona-nova-cyrillic-700-normal.woff2)format("woff2"),url(/fonts/files/bona-nova-cyrillic-700-normal.woff)format("woff");unicode-range:U+301,U+400-45F,U+490-491,U+4B0-4B1,U+2116}@font-face{font-family:Bona Nova;font-style:normal;font-display:swap;font-weight:700;src:url(/fonts/files/bona-nova-greek-700-normal.woff2)format("woff2"),url(/fonts/files/bona-nova-greek-700-normal.woff)format("woff");unicode-range:U+370-377,U+37A-37F,U+384-38A,U+38C,U+38E-3A1,U+3A3-3FF}@font-face{font-family:Bona Nova;font-style:normal;font-display:swap;font-weight:700;src:url(/fonts/files/bona-nova-hebrew-700-normal.woff2)format("woff2"),url(/fonts/files/bona-nova-hebrew-700-normal.woff)format("woff");unicode-range:U+307-308,U+590-5FF,U+200C-2010,U+20AA,U+25CC,U+FB1D-FB4F}@font-face{font-family:Bona Nova;font-style:normal;font-display:swap;font-weight:700;src:url(/fonts/files/bona-nova-vietnamese-700-normal.woff2)format("woff2"),url(/fonts/files/bona-nova-vietnamese-700-normal.woff)format("woff");unicode-range:U+102-103,U+110-111,U+128-129,U+168-169,U+1A0-1A1,U+1AF-1B0,U+300-301,U+303-304,U+308-309,U+323,U+329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Bona Nova;font-style:normal;font-display:swap;font-weight:700;src:url(/fonts/files/bona-nova-latin-ext-700-normal.woff2)format("woff2"),url(/fonts/files/bona-nova-latin-ext-700-normal.woff)format("woff");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Bona Nova;font-style:normal;font-display:swap;font-weight:700;src:url(/fonts/files/bona-nova-latin-700-normal.woff2)format("woff2"),url(/fonts/files/bona-nova-latin-700-normal.woff)format("woff");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
Graphite progress report (Q4 2024)
By Keavon Chambers & Hypercube. March 31, 2025.
Graphite, a new open source 2D procedural graphics editor, has wrapped up 2024 with a fourth quarter (October-December) focused on introducing quality of life features for both Graphite's interactive tools-driven and procedural editing workflows.
All Q4 2024 commits may be viewed in this list 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, be sure to check it out as well. If you'd like to help speed up future progress, please consider getting involved with code, QA/bug testing, or art/marketing projects. Donations are also valued, as are stars on GitHub. Follow along and partake in our Discord community, 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 and apply before April 8 at 18:00 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.
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 CtrlM (macOS: ⌘M) (#2097)
Text controls for line height, character spacing, and wrappable box areas that can be dragged with the Text tool (#2016, #2118)
Pinnable node sections in the Properties panel (commit e6d8c47)
Offset Path node that expands or contracts a vector shape (#2030)
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 (#2011, #2045)
Support for Fill and Stroke nodes with groups, applying to each vector layer within (#2046)
Switch node that routes one of two data connections based on a true or false value (#2064)
Bevel node that flattens the corners of vector shapes (#2067, #2096)
Jitter Points node that randomly offsets each point in a vector path (commit 7d86bf4)
Node insertion button, and layer renaming, directly from the Properties panel (#2072, #2081)
Path tool feature where pressing Space while dragging a handle makes the anchor be dragged as well (#2065)
Path tool feature where pressing Tab while dragging a handle makes it swap to the opposite handle (#2058)
Pen tool feature allowing the connection of layers by their endpoints so they both get merged into a single layer (#2076)
Clamp node that limits an input number between a minimum and maximum range (#2087)
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 (#2087)
Dot Product node that calculates the mathematical dot product between two numerical vectors (#2126)
Math node that calculates a custom math expression with variables "A" and "B" (#2121)
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 (commit d649052)
Freehand tool feature for drawing new subpaths on an existing vector layer by holding Shift (commit ed119ad)
Proper automatic placement of layers into the artboard they're drawn inside of (#2110)
Menu bar additions of Layer > New, Layer > Group Selected, and Layer > Delete Selected (commit feba874)
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 (#2162)
Path tool feature for snapping to 15° increments and locking the angles of dragged handles when Shift and Ctrl modifier keys are pressed (#2160)
Support for multiple top output wires extending from the same layer stack (#2049)
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 (commit 1264ea8)
Revamped quick measurement overlays now supporting every layer arrangement scenario (#2147, #2155)
Fixes
Fix for viewport tools no longer remaining active in the background when the node graph is open (#2093)
Fix to boolean operations so open subpaths are automatically closed (#2014)
Fix for a problem with double clicking an anchor for converting it between smooth and sharp (#2023)
Fix for broken gradient transforms with the Vello renderer (#2059)
Fix for alignment snapping not preserving aspect ratio when Shift is held (#2062)
Fix for the Text tool clearing the text when hitting Esc (#2052)
Fix for allowing the Path tool to edit an upstream vector path even if there's a type conversion midway (#2055)
Fix for the number input widget not updating its unit symbol when changing to show another input field (#2080)
Fix to make the Sample Points, Scatter Points, and Splines from Points nodes generate segments (not just invisible points) and work with subpaths (#2085)
Fix for sometimes breaking the selected layer upon switching away from the Select tool (commit 8d3da83)
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 (commit 12ca060)
Fix to make point nudging with the Path tool work in document space (#2095)
Fix to make the Spline node algorithm be continuous across start/end points (#2092)
Fix to properly support layer nudging when the view is tilted and make nudge resizing work in the Artboard tool (#2098)
Fix to disable menu bar entries when no layer is selected (#2098)
Fix for clarifying the present state of the Brush tool with a warning message (commit de366f9)
Fix to load the editor faster by moving font catalog loading to document creation time (commit de366f9)
Fix to make the Pen tool only append new paths when Shift is held (#2102)
Fix to make the Pen tool always snap to endpoint anchors, even when snapping is off (#2107)
Fix crash when upgrading a document with a Modulo node from 3 commits ago (commit 4c4d559)
Fix to clean up the consistency of the editor preferences dialog (commit 99cf8f0)
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 (#2116)
Fix for dragging a pair of colinear handles to break the colinearity so they can move without their anchor (#2120)
Fix for the broken bounding box of image layers, which also impacted their layer thumbnails (#2122)
Fix to restore the keyboard shortcut label in the menu bar's File > Close menu item (#2135)
Fix to the syntax of exported SVG files that minorly deviated from spec and may have impact some strict SVG viewers (#2131)
Fix for the UI by removing most "coming soon" elements left over from earlier times when placeholders were necessary (commit 1264ea8)