PranavandFredApps 3379f12a56 Data: Solver Sensitivity report (#501)
* Data: Solver Sensitivity report

Builds on the Answer and Limits reports. reports: [sensitivity] (on
data.solver and data.solverReport) writes a Sensitivity report for
models solved by Simplex LP or GRG Nonlinear without integer
constraints and with a Max or Min objective.

Simplex LP: the model is linearised at the solution and solved again,
and the report is read from the final tableau: each variable's reduced
cost and the range of its objective coefficient over which the basis
stays optimal, and each constraint's shadow price and the range of its
right side over which that price holds (1E+30 for no limit).

GRG Nonlinear: difference gradients at the solution give each binding
constraint's Lagrange multiplier (a least-squares fit on the variables
inside their bounds, dropping constraints whose multiplier has the
wrong sign) and each variable's reduced gradient.

The Solver Results dialog offers the report.

* Data: Solver Results offers the Sensitivity report only where it applies

Integer and binary models, the Evolutionary method and Value Of goals
have no Sensitivity report, and choosing it with other reports made the
whole report request fail. Solver Results now disables the Sensitivity
choice for such models (with the reason on hover) and leaves it out of
the request, so the other chosen reports are still written.

sensitivity_applies (in cmd::solver) reads the model from data.solver
params, as the dialog holds them; a test checks that it agrees with the
report for each kind of model.

* Data: Solver bounds the nonlinear Sensitivity report's work

The Lagrange multipliers of a GRG Nonlinear model are found by least
squares over the binding constraints; a constraint whose multiplier has
the wrong sign is dropped and the rest are solved again. Each pass costs
about 2·k²·f operations for k binding constraints and f free variables,
and each pass drops at most one constraint, so a model at the
1,000-constraint limit could take minutes and hold the window.

The passes now share a limit of about 10⁹ operations (about a second).
Past it the report is refused with a message, before anything changes:
the workbook and the Undo history stay as they were, as for the other
report refusals. The limit is lowered in tests so the refusal can be
reached with a small model.

* Data: Solver refuses a Simplex Sensitivity report for a point that is not optimal

The Simplex LP Sensitivity report solves the linearised model again to
read its final tableau, but shows the final values it was given.
data.solverReport accepts any values, so a report could pair values that
are not optimal (or not feasible) with the shadow prices, reduced costs
and ranges of the true optimum. The report now checks that the given
point satisfies every constraint and bound and reaches the optimal
objective (both within the constraint precision; another optimal point
with the same objective is accepted) and is refused otherwise, before
anything changes.

* Data: Solver compares the Sensitivity report's objective in both directions

The check that a point is optimal compared its objective with the
optimum in one direction only, as for an inequality. A point just
outside a bound (within the constraint precision) can still have an
objective far below the minimum when its costs are large, and that
point was accepted. The objective is now compared by the size of the
difference, so such a point is refused.

* Data: Solver refuses a nonlinear Sensitivity report for a point that is not optimal

data.solverReport accepts any finite variable values. The Simplex LP
report already refuses values that are not optimal, but the GRG
Nonlinear report described whatever point it was given: for example, a
minimisation of A1^2 given A1 = 1 found no binding rows and reported a
reduced gradient of 2 instead of requiring the optimum at A1 = 0.

The nonlinear report now checks, from the gradients it already
computes, that the values are a Karush-Kuhn-Tucker point before
anything changes: every constraint holds within the tolerance the
nonlinear method accepts a solution within, every bound holds within
the constraint precision, each variable inside its bounds has a reduced
gradient near zero, and a variable at a bound has a reduced gradient
that points only out of that bound for the objective's direction. A
binding row whose multiplier has the wrong sign is still dropped, and
the gradient it would have explained now causes the refusal. The
stationarity tolerance is 1E-3 of the largest objective-gradient
component (at least 1), well above the reduced gradients left at optima
the method solves.

* Data: Solver writes the other reports when the Sensitivity report does not apply to the solution

Since the nonlinear Sensitivity report refuses values that are not
optimal, data.solver with reports that include sensitivity failed as a
whole when the run stopped before an optimum (an evaluation limit) or
the values were not one, so the solution and the Answer and Limits
reports were lost. Solver Results also offered Sensitivity after any
solve, and ticking it with Answer after such a run wrote no report.

data.solver now writes the Sensitivity report only when the run reached
an optimal or converged solution that the report accepts. Otherwise the
solve still applies, the other reports are written, and the result
lists the report in skippedReports with the reason. Models that have no
Sensitivity report (integer, Evolutionary, Value Of) are refused as
before. data.solverReport still refuses values that are not optimal;
its new skipNotOptimal parameter leaves the report out instead. Solver
Results offers Sensitivity only after an optimal or converged status,
with the reason on hover, passes skipNotOptimal, and shows the reason
when the report is left out.

* Data: Solver skips a Sensitivity report in a protected workbook without refusing the call

Structure protection is now checked only when a report sheet will be written. A data.solverReport request with skipNotOptimal whose only report is skipped changes nothing, so it succeeds in a workbook whose structure is protected and returns skippedReports; a request that would write a sheet is still refused.

* Data: Solver refuses report sheets in a protected workbook before building them

A request that must write a report sheet (any Answer or Limits report, or a Sensitivity report that is not allowed to be skipped) is refused before any report is built, so a protected workbook does not run the Limits report's optimisations first. The check after the reports are built remains for a Sensitivity report that may be skipped.

* Data: Solver reports Simplex sensitivity only at the vertex it describes

The Simplex LP Sensitivity report solves the linearised model again and
takes its prices and ranges from that final tableau, but it discarded
the vertex the solve returned and showed the values it was given. When
the optimum is not unique, another optimal point passed the optimality
check, so the report paired those values with the ranges of a different
vertex. The report now also requires the given values to match the
re-solved vertex within the constraint precision, and refuses another
optimum with a message that says so. A Sensitivity report that data.solver
or Solver Results may skip is left out instead, as for other points the
report does not support. Values from a Simplex LP solve still match, so
the usual Solver Results flow is unchanged.

* Data: Solver gives a redundant constraint's right side no allowable change

The Simplex LP Sensitivity report ranges each right side over the basic
values of the real columns only. A row that the other rows make
redundant (max x with x = 2 and 2x = 4) keeps its artificial column
basic at zero after phase one, and changing either right side alone
moves that artificial, so no change has a solution. The report showed
1E+30 and 2 for the first row instead. When a basic artificial has a
nonzero entry in the column of the right side being changed, both
allowable changes are now 0. The objective-coefficient ranges are
unaffected: such a row has no cost and no entries in the real columns,
and a cost change does not affect feasibility.

* Data: Solver builds the Simplex Sensitivity report's program at the solve's start values

The Simplex LP solve linearises the model at the start values, but the
Sensitivity report linearised it again at the solution. The difference
coefficients then differ in their last bits, and when the optimum is
not unique that can break a tie between pivots the other way: the
report's solve returned the other vertex and refused the solve's own
values. With max 0.1x + 0.1y over a tied edge, more than half of the
sampled starting points were refused.

The solve and the report now build the program with one function,
simplex_program, and the report builds it from the start values it is
given (data.solver's start values, which Solver Results passes back as
original), so its solve repeats the solve's pivots and returns the same
vertex. Start values at which the model cannot be evaluated fall back
to the solution, and values that are not the solve's are still refused
by the vertex check.

* Data: Solver's Sensitivity report sets its noise levels from the coefficients, not the objective's value

The figures a Sensitivity report prints as 0 were those below 1E-9 of the objective's value (1E-6 for the nonlinear figures), so a constant in the objective hid real prices: max 1E9 + x with x <= 10 wrote the row's shadow price, exactly 1, as 0, although the constant changes nothing about the optimisation. A shadow price and a reduced cost are combinations of the objective coefficients, so Simplex LP now takes the noise level from the largest coefficient; a reduced gradient is a part of the objective's gradient and a Lagrange multiplier objective per unit of its row's gradient, so GRG Nonlinear takes it from the gradient's size, and the multipliers' sign test compares lambda times the row's gradient size with it. Allowable changes keep the tableau's pivot tolerance as before.

* Data: Solver Sensitivity report tests for variables at a bound and for both goals

Hand-derived cases pin the report's conventions. Simplex LP: a maximum's variable at its upper bound (reduced cost 1, Allowable Increase 1E+30, Allowable Decrease 1), a minimum with a >= row and a variable at its upper bound (reduced cost -1, Allowable Increase 1, Allowable Decrease 1E+30), and Winston's Dakota Furniture problem, whose variable at zero has reduced cost -5 with Allowable Increase 5 and Allowable Decrease 1E+30, with every shadow price and allowable range worked out from the final basis. GRG Nonlinear: the Lagrange multiplier is the derivative of the optimal objective with respect to the right side and the reduced gradient that with respect to the variable's bound for a minimum (-1) and the negated maximum (+1), and the mirror image of the existing minimum with a variable at its bound (multiplier +2, reduced gradient -4).

* Docs: ROADMAP.md records the Solver Sensitivity report

The detail documents (docs/gaps.md, docs/target-app-parity.md) record the report, but the roadmap summary did not: its header change note, progress log and revision history now carry it.

* Data: Solver's Sensitivity report bounds each figure's rounding noise by its own terms, not the model's largest coefficient or gradient

The noise level a figure had to exceed to print was 1E-9 of the largest objective coefficient (Simplex LP) or 1E-6 of the largest gradient component (GRG Nonlinear) for every figure, so an unrelated large coefficient hid real values: max 1E9*x + y with x <= 1 and y <= 1 as rows wrote the second row's shadow price, exactly 1, as 0, and the same model's Lagrange multiplier 1 and a third variable's reduced cost or gradient -1 went the same way. The rounding error of a sum scales with the sum of its terms' sizes, so each figure now has its own bound from the terms it is made of. Simplex LP: a shadow price y_i = sum over the basis rows of c_B(r) * Binv(r, i) is bounded by 1E-9 of those terms' sizes (the row's unit column of the final tableau holds Binv's column i); a reduced cost cf_j - sum of a_ij * y_i by 1E-9 of |cf_j| plus |a_ij| times each price's term sizes, so a price that is a small difference of large terms carries their error into the reduced costs it is used in. GRG Nonlinear: a multiplier solved from its row of the normal equations is bounded by 1E-6 of its own terms (the row's gradient times the objective's, and its coupling terms with the other active rows times their multipliers) over the row's squared norm; the multipliers' sign test uses the same bound, so a row leaves the active set on the criterion the report rounds with; a reduced gradient is bounded by 1E-6 of its own component and the multipliers' terms plus the multipliers' bounds times |g_ij|. Nearly parallel active gradients amplify every multiplier's error beyond a bound local to a row; such multipliers print as computed. A constant in the objective still changes no bound, since none depends on the objective's value. The nonlinear method stops on the 1E9 model from (0, 0) with "unbounded", so its two regression tests ask for the report at the optimum through data.solverReport.

* Data: Solver's Sensitivity report tests a nonlinear point's optimality by each variable's and row's own scale, not the model's largest

The Karush-Kuhn-Tucker check that refuses a Sensitivity report for a point that is not optimal accepted a reduced gradient within 1E-3 of the largest gradient component, and a row violation within ten times the constraint precision of the largest constant right side. Both scales were the model's, not the figure's: on max 1E9*x + y with x <= 1 and y <= 1 as rows the tolerance was 1E6, so (1, 0.5), where raising y gains 1 per unit, passed as optimal and the report was written for it; on max y - (x - 2)^2 with x <= 1E9 and y <= 1, (2, 1.0001) passed although the second row is over by 1E-4 against its own precision of 2E-5. Each reduced gradient is now tested against 1E-3 of the terms it is made of (the objective's component and the multipliers' parts, at least 1, the same floor as before), and each row against ten times the constraint precision of its own right side. The nonlinear method itself accepts a solution relative to the largest constant right side; a row over by more than its own precision is not satisfied whatever the other rows' sizes, so the report refuses it. The term sizes are shared with the reduced gradients' rounding bound. Two regression tests, each proving the workbook is left as it was and that the optimum is still reported.

* Data: Solver refuses a Sensitivity report the model has none of before any report is built or the solve runs

The refusals that apply to the model rather than the solution (integer or binary constraints, the Evolutionary method, a Value Of goal) were made inside the report loop, when it reached the Sensitivity report, so a request listing the Limits or Answer report first built those reports for an error: a Value Of model asking for limits and sensitivity ran the Limits report's two optimisations per variable and received only the refusal, and data.solver ran its whole solve before it. prepare_reports now checks the model's refusal before the loop, in both modes (a refusal of the model was already an error in both; only the solution's not-optimal refusal is skipped under skipNotOptimal), and data.solver checks it before the solve. The regression test asks for limits and sensitivity on an integer model, which now gets the Sensitivity refusal instead of the Limits report's, and on a Value Of model, which gets the Sensitivity refusal with no sheet written.

---------

Co-authored-by: FredApps <FredApps@users.noreply.github.com>
2026-11-02 09:46:01 +01:00
2026-10-26 14:13:04 -06:00
2026-11-01 12:18:01 -05:00
2026-11-01 13:57:31 -05:00
2026-11-01 13:57:31 -05:00
2026-10-25 14:55:00 -04:00
2026-10-11 15:13:02 -03:00

ArtCraft

GridCraft icon: a spreadsheet page on a green tile

GridCraft

Spreadsheets and data; an open-source, high-performance modern spreadsheet application, built in pure Rust.

A fast, open-source, clean-room take on modern spreadsheet workflows. It runs natively on macOS, Windows, Linux and BSD, and in the browser via WebAssembly.
By the ArtCraft team.

Written in Rust Runs on macOS, Windows, Linux, BSD and the web License: MIT OR Apache-2.0 Agent-drivable over MCP
Scanned by SonarCloud Reviewed by CodeRabbit

Join the ArtCraft community on Discord

GridCraft on getartcraft.com · ArtCraft · All Crafting Apps


GridCraft showing a quarterly sales dashboard: a styled table with banded rows, filter buttons, a totals row, data bars, arrow icons and sparklines, next to a clustered column chart and a pie chart
Quarterly Sales Dashboard: a table with a totals row, conditional formatting, sparklines and two live charts — built entirely from GridCraft commands (File → New → Sample).

Note

ArtCraft is a community of artists from all walks of life. Painters, photographers, filmmakers, illustrators, designers, animators, hobbyists, and people who picked up a pencil last week. If you make things, you're one of us. Come say hi on Discord.

A look around · Why GridCraft · Quick start · For agents · Architecture · Roadmap · The Crafting Apps · License and credits

A look around

A household budget with accounting number formats, red highlights for overspent categories, a green-yellow-red colour scale on the percent-used column, a totals row with a double border, and a horizontal bar chart comparing budget with actual

Formatting. Accounting formats, cell styles, conditional rules and colour scales, frozen headers and a bar chart.

A gradebook table with averages and letter grades computed with IFS, green highlights on A grades and data bars, with the Formulas ribbon tab open showing the function library

Formulas. 500+ functions, dynamic arrays, LET and LAMBDA, structured table references.

The sales dashboard in dark mode with the Insert ribbon tab open, showing Pivot Table, Table, Pictures, Shapes, chart, sparkline, link, comment, text box and symbol buttons

Dark mode and the Insert tab: tables, charts, sparklines, pictures, shapes, links and comments.

A pivot table summarising revenue by region and product with a Channel report filter, grand totals and currency formatting, with the Pivot Table Fields pane open on the right showing ticked fields and the Filters, Columns, Rows and Values areas

Pivot tables. Field list, filters, layouts, date grouping and show-values-as, saved to and read from XLSX.

The sales dashboard with the column chart selected: resize handles on the chart, its source data outlined in the table, the Chart Design tab open and the Format Chart Area pane on the right

Charts. Seventeen chart types, Chart Design tab and a Format pane; selecting a chart outlines its source data.

The household budget in Page Break Preview: the print area outlined in blue with a dashed page break, Page 1 and Page 2 watermarks, and the area outside the print range greyed out

Page Break Preview, print settings, and PDF export and printing.

Why GridCraft

  • Familiar. Ribbon, formula bar, Name Box, sheet tabs, keyboard shortcuts, fill handle, point-mode formula entry with coloured references, Format Cells, Paste Special, conditional formatting, tables, charts and more. Everything is right where you expect it.
  • Compatible. XLSX is the native format: styles, themes, formulas (including dynamic arrays), tables, conditional formats, validation, comments, hyperlinks, charts, pictures, sparklines, pivot tables and print settings round-trip. CSV and TSV too. Legacy 97-2003 .xls workbooks open with their values, formulas and number formats (formatting and objects not yet). XLSB worksheet data can be imported, using saved values instead of formulas. ODS worksheet data can be imported, using saved values instead of formulas. Static pictures can live inside cells, fitting their size and moving with the data.
  • Capable. A dependency-graph calculation engine with dynamic arrays and spilling, 500+ worksheet functions plus LET, LAMBDA, MAP, REDUCE, SCAN, BYROW, BYCOL and MAKEARRAY, structured table references, a comprehensive number-format language, sort and AutoFilter, Flash Fill, data validation, outlining and subtotals.
  • Fast. Copy-on-write workbooks (undo snapshots are nearly free), incremental recalculation, and a virtualised grid that only lays out what's on screen.
  • Agent-native. Every ribbon button, menu item, dialog, keystroke and mouse gesture is a command that agents can run over a JSON control channel, an MCP server, or the CLI.
  • Everywhere. One Rust codebase for desktop and the web. No subscription, no licence server, no telemetry.

Quick start

cargo run --release -p gridcraft                              # desktop app
cargo run --release -p gridcraft -- --sample sales            # open a sample (sales, budget, grades)
cargo run --release -p gridcraft -- book.xlsx                 # open a workbook
cargo run --release -p gridcraft -- --sample sales --control 7979   # + JSON control channel
cargo xtask ci                                                 # fmt, clippy, tests, assets, layering, wasm

Choose Español under View → Interface language for Spanish formula names, decimal commas, and semicolon arguments. Workbooks, clipboard exchange, and programmatic APIs retain canonical English syntax.

Web

cd apps/gridcraft-web && trunk build --release     # → dist/web (serve it with any static server)
cd apps/gridcraft-web && trunk serve --release     # http://127.0.0.1:8771  (?sample=sales opens a sample)

You need trunk and the wasm32-unknown-unknown target. Open uses the browser's file picker (dropping files works too) and Save downloads the workbook.

Ribbon access keys

Press and release left Alt on Windows/Linux/BSD, or press F10 on any platform, then type a sequence below one key at a time. Keytips appear on the supported Home controls and menu items. On macOS, use F10 (Fn+F10 if required by your keyboard); Option remains available for entering accented characters.

Sequence after Alt/F10 Action
H A L / H A C / H A R Align left / center / right
H V S or legacy E S Open Paste Special
H O I or legacy O C A AutoFit selected columns

In Paste Special, T/V/F/C select formats/values/formulas/comments, and E toggles Transpose. Enter applies the selected options; Escape cancels the dialog. These letters also work with Alt held on Windows/Linux/BSD.

Escape backs out one keytip level; Alt/F10 again exits keytips. Clicking, scrolling, or switching windows cancels navigation. Access keys do not start while editing text or using another dialog. This is initial support for these Home and legacy paths; other ribbon commands and full mouse-free navigation remain to be implemented. Browsers or desktop environments may reserve Alt/F10 for their own menus.

Logs

The desktop app writes its log to standard error and to gridcraft.log in the logs folder next to its settings: ~/.config/gridcraft/logs on Linux and BSD ($XDG_CONFIG_HOME/gridcraft/logs when set), ~/Library/Application Support/GridCraft/logs on macOS and %APPDATA%\GridCraft\logs on Windows. Each start moves the previous log to gridcraft.1.log and that one to gridcraft.2.log, so the log of a run that crashed survives the next start; attach them to a bug report. A log file stops growing at 16 MiB. By default GridCraft's own crates log at info and everything else at warn; RUST_LOG replaces that with env_logger-style directives, e.g. RUST_LOG=debug or RUST_LOG=warn,gridcraft_engine=debug (a trailing * matches a prefix: gridcraft*=debug). Runs with GRIDCRAFT_NO_PREFS set (no saved settings) log to standard error only.

For agents: MCP, CLI and the control channel

claude mcp add gridcraft -- gridcraft-cli mcp                 # headless MCP server
gridcraft-cli mcp --connect 7979                               # MCP bridged to a running window
gridcraft-cli eval '=XLOOKUP("b",{"a","b"},{1,2})'             # → 2
gridcraft-cli run --sample budget --cmd 'home.bold={"range":"B4:F4"}' --out budget.xlsx
gridcraft-cli cat budget.xlsx --range B4:F15                   # aligned text table
gridcraft-cli commands --search chart                          # every command and its params

The MCP server offers tools such as read_range, write_range, set_cell, evaluate_formula, format_range, insert_chart, create_table, sort_range, filter and execute_command (which reaches every one of GridCraft's commands). The desktop app's control channel adds real pointer and keyboard input, dialogs and screenshots. See docs/mcp.md, docs/cli.md and docs/control-protocol.md.

Architecture

GridCraft is an engine-first Cargo workspace with enforced layering (cargo xtask layers). The egui frontend is a separate crate, so the UI can be swapped without touching the engine.

Layer Crates
L0 core (addresses, values, errors, dates, input parsing) · numfmt (number format codes)
L1 formula (lexer, parser, printer, reference adjustment) · functions (worksheet function library)
L2 model (workbook, sheets, copy-on-write cells, styles, tables, charts…) · calc (dependency graph, evaluator, dynamic arrays) · xlsx (XLSX, CSV, plus XLSB and ODS data import)
L3 chart (toolkit-free chart layout and rendering)
L5 engine (session, commands, history, clipboard, fill, sort, filter, file I/O)
L6 ui-egui (spreadsheet UI, control channel) · mcp (MCP server)
L7 apps/gridcraft, apps/gridcraft-cli, apps/gridcraft-web
  • Contributor and agent rules (clean-room, asset policy, never-crash, quality gates): AGENTS.md
  • Bundled assets: each one is listed with its licence in ATTRIBUTION.md
  • Releases: signed macOS, Windows, Linux, BSD and web builds; see docs/releasing.md

Roadmap

GridCraft is in alpha: the core workflow works end to end, but depth, fidelity and file compatibility are still rough. It covers 89% of standard ribbon commands in our catalog (docs/parity-checklist.md) and 513 standard worksheet functions (docs/function-parity.md); this is a parity inventory, not a count of working implementations. Weighted by what spreadsheet users rely on, we estimate it is about 50% ready for real work. Next up: XLSX fidelity on real-world files, very large workbooks and multi-threaded recalc, chart depth, text in every script, and .xls import. Stage, numbers and estimates are in ROADMAP.md; the ranked work list is docs/gaps.md.

Downloads

Download GridCraft from GitHub: the latest release has every build listed below, and all releases has earlier versions and their notes. <ver> in the file names is the version number, and SHA256SUMS.txt lists a checksum for every file.

Windows

Build Installer Portable
x64 (64-bit Intel/AMD) gridcraft-<ver>-windows-x64.msi gridcraft-<ver>-windows-x64-portable.zip
arm64 (Snapdragon and other ARM PCs) gridcraft-<ver>-windows-arm64.msi gridcraft-<ver>-windows-arm64-portable.zip
x86 (32-bit) gridcraft-<ver>-windows-x86.msi gridcraft-<ver>-windows-x86-portable.zip

Installers and executables are code-signed. The MSI installer offers an optional desktop shortcut (unchecked on a new installation).

If the app doesn't open on Windows: the desktop app initializes only DirectX 12 by default. Letting wgpu also create an OpenGL instance can crash some graphics drivers (AMD's atio6axx.dll) before the window appears, so the app would flash in Task Manager and quit. WGPU_BACKEND overrides the default for troubleshooting (for example dx12 or vulkan). In PowerShell, from the folder containing the executable:

$env:WGPU_BACKEND = "vulkan"
& .\gridcraft.exe
Remove-Item Env:WGPU_BACKEND                     # restore the default for later launches

An explicit gl override can bring the driver crash back on affected systems, and vulkan can bring back a crash in some Intel UHD Graphics drivers (igvk64.dll, #120). The macOS, Linux and web backend defaults are unchanged.

If the app is slow on an older Windows PC, or DirectX 12 can't start: when DirectX 12 finds no graphics card with a driver for it, it only offers the Windows-provided software renderer, which draws every frame on the processor. In that case GridCraft uses OpenGL instead, without setting anything; if the first renderer fails to start, it tries the other one once. The log file records the choice (lines starting with graphics:). WGPU_BACKEND still overrides it (and then nothing else is tried), e.g. $env:WGPU_BACKEND = "gl" or "dx12".

DirectX 12 shaders are compiled with FXC, which is part of Windows, so no dxcompiler.dll or dxil.dll is needed, and a copy another program put on PATH can no longer stop the app at start ("Parent device is lost", #339). $env:WGPU_DX12_COMPILER = "dxc" (or "auto") selects wgpu's DXC handling again for troubleshooting.

macOS

Build File Notes
App, universal (Apple silicon + Intel) gridcraft-<ver>-macos-universal.dmg Signed and notarized
Command-line tool, universal gridcraft-cli-<ver>-macos-universal.zip Signed and notarized

Linux

Format x86_64 aarch64 (ARM64) Notes
AppImage gridcraft-<ver>-linux-x86_64.AppImage gridcraft-<ver>-linux-aarch64.AppImage Runs anywhere; updates itself with AppImageUpdate (.zsync files)
Flatpak gridcraft-<ver>-linux-x86_64.flatpak gridcraft-<ver>-linux-aarch64.flatpak Sandboxed; flatpak install --user <file>
Debian/Ubuntu gridcraft-<ver>-linux-x86_64.deb gridcraft-<ver>-linux-aarch64.deb
Fedora/RHEL/openSUSE gridcraft-<ver>-linux-x86_64.rpm gridcraft-<ver>-linux-aarch64.rpm
Tarball gridcraft-<ver>-linux-x86_64.tar.gz gridcraft-<ver>-linux-aarch64.tar.gz Unpack anywhere

FreeBSD

Build File
x86_64 gridcraft-<ver>-freebsd-x86_64.tar.gz

Web (WebAssembly)

Build File Notes
Static site gridcraft-web-<ver>.zip Runs in a modern browser; host it on any static server

The Crafting Apps

GridCraft is one of the Crafting Apps: free, open-source creative tools from the ArtCraft team, each written from scratch in Rust and each able to stand on its own.

App What it's for Code Learn more
PhotoCraft Image editing: layers, masks, type and real PSD files GitHub Website
VectorCraft Vector illustration GitHub Website
FilmCraft Video editing, color and sound GitHub Website
LightCraft Photo library and raw development GitHub Website
PdfCraft Reading, organizing and protecting PDFs GitHub Website
EffectCraft Motion graphics and visual effects GitHub Website
DesignCraft Page layout and publishing GitHub Website
FigCraft Interface design and prototyping GitHub Website
WordCraft Word processing and documents GitHub Website
SoundCraft Audio recording and editing GitHub Website
DeckCraft Presentations and slide decks GitHub Website
CADCraft Computer-aided design GitHub Website
GridCraft Spreadsheets and data GitHub Website
TextureCraft 3D texture painting GitHub Website
Craft Anything See how any software works, then build your own GitHub Website
SolidCraft Parametric 3D CAD by Tangency Labs (community app) GitHub

And ArtCraft itself, our AI image and video studio for artists who want real control.


Join the ArtCraft community on Discord

Come make things with us

Our Discord is where artists of every kind hang out: people who paint, shoot, draw, cut film, set type, and people still figuring out what they like to make. Share what you're working on, ask for help, tell us what's broken, or tell us what you wish these tools could do. Whatever your medium and however long you've been at it, you're welcome here.

discord.gg/artcraft · getartcraft.com · The Crafting Apps · GridCraft

License and credits

GridCraft is dual-licensed under MIT or Apache-2.0, at your option. Copyright (c) 2026 ArtCraft Team and the GridCraft contributors. Required notices are in NOTICE.

Bundled fonts, icons, images and other assets keep their own open licenses; each one is listed with its author, source and license in ATTRIBUTION.md.

All UI icons are drawn in code and are original; the sample workbooks and every screenshot in this README were made with GridCraft itself. GridCraft bundles no fonts: it uses the fonts installed on your system.

The ArtCraft name, wordmark and logos in docs/brand/ are trademarks of the ArtCraft Team and are not covered by this license. They may be used only unmodified, and only as part of this repository and GridCraft, under docs/brand/LICENSE-brand.txt. Forks and modified versions must remove them.


ArtCraft
Made by the ArtCraft team and community.

S
Description
No description provided
Readme
122 MiB
0 Stars 1 Watchers 0 Forks
Languages
Rust 94.9%
RenderScript 4.2%
PowerShell 0.5%
Shell 0.3%