Getting started with Dither
Have you ever made the same edit twelve times, then wished you could change all twelve at once? Or found a combination of settings you loved and wanted to reuse next week?
That is what Dither is for.
Dither is Satin Studio's built-in language for describing a job. You can use it for tiny calculations, repeatable edits, design variations, and longer recipes. The regular buttons and menus still do the everyday work. Dither becomes handy when you want to say, “Do this exact thing again,” without clicking through the same steps.
You do not need programming experience. We will start with one line and build from there. When you want exact syntax and edge-case rules, keep the Dither Language Specification nearby.
Your first expression
A Dither expression is wrapped in parentheses. The first word says what to do; the remaining values are the ingredients.
; Add 2 and 3. The result is 5.
(+ 2 3)
Try multiplication:
; Multiply 6 by 4. The result is 24.
(* 6 4)
Comments begin with a semicolon. Dither ignores comments, so use them freely to leave notes for your future self.
Keep a few values together
Square brackets make a vector: a small ordered row of values. Think of it as a tray whose items stay in the order you placed them.
; Keep three thread names in a predictable order.
["Deep Teal" "Bone Ivory" "Antique Gold"]
Curly braces make a map: a set of labeled values. A map is like a form with a name beside each blank.
; Describe a hoop by labeling its width and height.
{:width-inches 5.0
:height-inches 7.0}
Maps can contain other maps. That is useful when a setting has a few related parts.
; Store the hoop measurements inside a machine profile.
{:machine :brother-se2000
:hoop {:width {:inches 5.0}
:height {:inches 7.0}}
:export-format :pes}
Words that begin with a colon, such as :machine and :pes, are keywords. They are short, typo-resistant labels rather than prose.
Give a value a useful name
let gives temporary names to values. The names live only inside that expression.
; Name the width and height, then calculate the hoop area.
(let {:width 5.0
:height 7.0}
(* width height))
The result is 35.0. Naming the values makes the calculation easier to read and change.
Make a reusable helper
When you repeat the same calculation, give it a name with define and fn.
; Keep a number between a lower and upper limit.
(define user/clamp
(fn [number lower upper]
(min upper (max lower number))))
; 2.4 is above the upper limit, so this returns 2.0.
(user/clamp 2.4 1.0 2.0)
The user/ prefix gives your helper its own tidy namespace. It also makes your helpers easy to distinguish from built-in tools.
Repeat a small job
range produces a bounded sequence of whole numbers. for runs one small expression for each number.
; Make four angle records: 0, 45, 90, and 135 degrees.
(for [index (range 4)]
{:index index
:angle-degrees (* index 45)})
Dither puts a firm limit on synchronous work. An accidental giant range stops with a clear resource-limit message instead of freezing Satin Studio. Expensive jobs such as compiling stitches run in the background.
Save a typed design setting
defcustom creates a named setting with a known kind of value. Satin Studio can inspect this information and present the setting consistently.
; Save two controls for a family of Firebird variations.
(defcustom user/firebird-art
{:seed 208319
:primary-wing-feathers 9}
:title "Firebird Artistic Controls"
:type :map)
Read the setting with custom/get:
; Read the complete Firebird settings map.
(custom/get user/firebird-art)
Read one nested value with get-in:
; Follow the :seed label inside the Firebird settings.
(get-in (custom/get user/firebird-art) [:seed])
Try a variation without losing the original
A temporary override is perfect for asking, “What would this recipe do with a different seed?”
; Use seed 208320 only inside this expression.
(custom/with-temporary
{user/firebird-art
(assoc (custom/get user/firebird-art) :seed 208320)}
; Read the temporary seed. The original returns afterward.
(get-in (custom/get user/firebird-art) [:seed]))
The temporary value disappears when the expression finishes, leaving the saved setting unchanged.
Run a Satin Studio command
Dither commands are the same actions used by Satin Studio's controls. Their generated reference pages show the exact inputs each command accepts.
; Give the open project a visible name.
(project/set-name {:name "Garden Sampler"})
; Compile the open project as a background job.
(project/compile)
Use the generated command reference instead of guessing command names. If an input is required, its page shows the name, kind, and an example form.
The complete Recursive Firebird
The Recursive Firebird is a five-thread phoenix for a 5 × 7 garment hoop. It is deliberately ambitious: one program grows the motifs, protects open fabric, budgets stitches, plans five machine passes, checks production limits, renders a proof, saves the editable design, and exports PES.
The complete source is split only to keep each downloadable file comfortable to inspect:
- Firebird source, part 1 contains the module, settings, fields, skeleton, feather grammar, wings, tail, body, and filigree.
- Firebird source, part 2 contains negative space, perception, sheen, physics, stitch compilation, routing, verification, commands, and export.
Run part 1 and then part 2 in the same Dither session. Installed modules named in the module header supply the vector, field, grammar, embroidery, rendering, and job operations. Core language forms such as define, fn, let, for, if, maps, vectors, and arithmetic follow the language specification.
If this is your first long recipe, begin with the smaller Recursive Firebird basics. Then use the tour below as a map through every numbered section of the full program.
The module header: pack the toolbox
The opening module form names the recipe, records its version, lists the modules it uses, and requests the abilities needed to write the document, run jobs, adjust the workspace, and save files.
; Name this recipe and list the toolboxes used by later sections.
(module dither://package/user/recursive-firebird
:title "The Recursive Firebird"
:version "0.1.0"
:requires [dither://package/vector
dither://package/art.grammar
dither://package/embroidery
dither://package/render]
:capabilities [:write-document :spawn-jobs :write-files])
The complete header lists every specialized module rather than the shortened four-module sample above. This makes a missing dependency visible before a long generation job begins.
1. Set the production envelope
Three typed settings describe the machine, garment, and hard limits. The machine map records hoop and safe-area dimensions. The garment map makes black jersey an active part of the design. The budget map sets stitch, trim, jump, density, gap, satin-width, and layer limits.
The distinction between target and hard maximum matters. A solver may trade a few extra stitches for a clearer eye, but it must never cross the hard ceiling.
; Aim near 34,000 stitches but reject any result above 42,000.
{:target-stitches 34000
:hard-max-stitches 42000
:target-trims 28
:hard-max-trims 45}
2. Choose five actual threads
The palette contains exactly five upper threads. Each entry carries an ID, visible name, RGB preview, and artistic roles. Black comes from the shirt, so it creates shadow and separation without becoming a sixth thread.
The separate thread-order vector fixes the machine sequence: teal, plum, coral, ivory, then gold. Gold finishes last because it contains the veins, outline, and solar focal point.
3. Gather the artistic controls
user/firebird-art is the creative control panel. It stores the seed, pose, overall dimensions, solar-core size, left/right relationship, detail focus, viewing-distance goals, open-fabric target, recursion depth, and motif counts.
The :paired-symmetry and :independent-mutation values add to 1.0. Corresponding feathers therefore share most of their development while retaining a small private variation.
4. Build tiny math helpers
The recipe defines clamp, lerp, smoothstep, polar conversion, point addition, point scaling, mirroring, and palette lookup. These helpers turn later formulas into readable design sentences.
; Move smoothly from a to b as t travels from 0.0 to 1.0.
(define user/lerp
(fn [a b t]
(+ a (* (- b a) t))))
The interpolation is \(L(a,b,t)=a+(b-a)t\). At \(t=0\) it returns \(a\); at \(t=1\) it returns \(b\); values between them slide smoothly from one end to the other.
smoothstep first clamps \(t=(x-a)/(b-a)\) to the interval \([0,1]\), then evaluates \(t^2(3-2t)\). The curve starts and ends gently, which keeps a field transition from forming a visible corner.
A helper should do one unsurprising job. That makes it easy to test before using it hundreds of times inside a grammar.
5. Make paired variation repeatable
user/paired-value asks art/paired-random for one shared value and one side-specific value. It blends them using the symmetry control, then maps the result into a useful range with lerp.
If \(p\) is the pairing amount, \(s\) is the shared sample, and \(q\) is the side-specific sample, the blend is \(u=ps+(1-p)q\). With \(p=0.82\), most of the value comes from shared development and 18 percent comes from the individual side. lerp then maps \(u\) from the unit interval into the requested length, width, or sweep range.
Because every call includes the seed, label, index, and side, regenerating seed 208319 produces the same anatomy. Changing only the seed creates a related bird rather than random noise.
6. Define the global fields
Four functions make distant parts of the bird respond to the same invisible environment:
solar-heatmeasures influence from the solar core;rising-flowcombines radial angle, curl noise, and upward motion;light-directionpoints toward the virtual light;detail-importancescores places that deserve stitch budget.
light-direction uses \(\operatorname{atan2}(y-y_c,x-x_c)\) to find the angle from the solar center \((x_c,y_c)\) to a design point. Unlike a simple slope, atan2 keeps the correct quadrant all the way around the sun.
The rising field is a weighted sum: 48 percent radial angle, 33 percent curl noise, and 19 percent upward motion. The weights add to 1, making their relative influence easy to reason about.
These fields are why the wing, body, tail, sheen, and detail allocation feel related.
7. Draw a semantic skeleton
user/firebird-skeleton returns a map of Bézier paths and landmarks: spine, neck, head, upper and lower wings, tail axis, and solar core. The skeleton is not stitched. It is a scaffold that tells later generators where anatomy lives.
Because the scaffold is ordinary data, you can inspect or render it before growing a single feather.
8. Compile the feather grammar
The feather grammar is the heart of the recipe. Its axiom begins one feather. Rules emit a gold rachis, paired barbs, optional eye flames, and a small recursive leaf only where importance and size justify it.
Growth constraints reject features that are too thin, gaps that are too tight, satin outside the allowed width, recursion beyond the chosen depth, or growth that would violate the budget.
; Add micro-leaves only when they are large and important enough to survive.
{:when '(and (> :importance 0.72)
(> :base-length 0.28))
:repeat {:count 3
:index :j
:body [:micro-leaf {:length (* 0.22 :base-length)}]}}
Quoted conditions are grammar data. The grammar engine evaluates them while expanding a motif, not while reading the outer settings map.
9. Place both wings
user/generate-wing selects the left or right skeleton paths and grows two rows:
- large rising primary feathers along the upper wing;
- smaller coverts along the lower wing.
For feather index \(i\) among \(n\) feathers, the upper-wing position is \(t=(i+0.65)/n\). The 0.65 offset avoids planting the first feather exactly at the path endpoint. vector/point-at evaluates the anchor at \(t\), while vector/tangent-angle supplies the local direction.
Each loop then calculates paired sweep, length, width, heat, importance, accent, and eye placement. The two sides share one function; side changes direction and private mutation without duplicating the whole algorithm.
10. Fan the tail streamers
tail-origin distributes roots across the body base. tail-direction fans them downward. generate-tail makes center streamers longer, alternates teal and coral accents, and gives three selected streamers eye motifs.
The normalized fan coordinate is \(u=i/(n-1)\), so the first streamer receives \(u=0\) and the last receives \(u=1\). Distance from the center controls length and width: central feathers approach 3.20 inches, while outer feathers approach 2.18 inches.
The resulting tail is a family of field-guided feathers, not eleven copies of one shape.
11. Construct the body, head, and sun
The body uses open contour lanes so shirt fabric remains visible. A gold bean-stitched spine connects the major anatomy. Separate shapes create the head, beak, eye, glint, solar core, and twenty-one irregular rays.
The solar core uses a radial fill and becomes the visual anchor. Rays use deterministic random lengths and skip every third position to avoid a rigid wheel.
12. Grow botanical flame filigree
A second grammar grows sparse flame-and-fern sprigs around the silhouette. Its recursive branches alternate sides and become shorter at each depth. generate-filigree distributes eighteen sprigs from the lower tail to the upper wings.
The filigree grammar has its own minimum feature, minimum gap, recursion, and budget rules. Decorative detail therefore obeys production limits just like the feathers.
13. Carve with negative space
user/apply-negative-space treats the black shirt as a palette color. It opens gaps between barbs, replaces dark fills with contours, removes hidden understitching, cuts shadow windows, and thins low-importance areas.
Protected roles—eye, beak, solar core, body spine, major rachises, and feather eyes—cannot disappear during carving.
14. Check three viewing distances
The perceptual optimizer renders the design for close craft inspection, normal viewing, and across-the-room silhouette. It protects focal order while allowing low-value micro-motifs to disappear, gaps to widen, and overlapping barbs to shorten.
This step answers a practical question: does the bird still read as a bright rising phoenix after clever details are reduced to actual thread?
15. Solve five-thread color and sheen
A continuous procedural heat field can suggest many colors, but the machine receives five real threads. embroidery.sheen/solve maps the field to those threads and adjusts stitch angle around a virtual light.
Directional thread reflection supplies apparent highlights without adding colors or requiring metallic thread.
16. Prewarp for the shirt
The physics module loads the calibration swatch for the actual jersey and stabilizer. If no measured swatch is available, the garment map supplies conservative stretch and pull values.
prewarp then compensates for pull, push, jersey stretch, layered shrinkage, and directional drift. Corrections are capped at 1.25 mm, with the eye, head, sun, and body receiving more protection than outer filigree.
17. Spend stitches where they matter
The level-of-detail compiler aims for 34,000 stitches and enforces the 42,000 ceiling. Every role receives a priority. Eye, glint, beak, sun, and head keep detail; micro-leaves and filigree simplify first.
The compiler also enforces stitch length, satin width, minimum gap, allowed stitch families, pass counts, and the rule that radial fill belongs only to the sun.
18. Plan five machine passes
The routing solver ignores drawing order and builds exactly five global color blocks. Within each pass it minimizes trims and jumps, preserves layering, hides travel under later stitches, avoids crossing open shirt, and ensures gold finishes last.
The route may bridge under future stitching or travel near an edge, but hidden bridges are capped at 7.5 mm and jumps must respect the production envelope.
19. Simulate and verify production
user/verify-production simulates needle penetrations, tension, stretch, stabilizer load, density, satin nesting, jumps, trims, registration drift, and sheen.
The report must confirm safe-area fit, no more than five colors, exactly five blocks, stitch and trim counts below their ceilings, no density or minimum-feature violations, and acceptable pucker risk. In compact form, the main hard checks include \(S\leq42{,}000\) stitches, \(T\leq45\) trims, and exactly five color blocks. A failed assertion links to production-verification Help instead of quietly exporting a risky file.
20. Orchestrate the main command
The main command turns all the helpers into one undoable background job. It creates the design, attaches the skeleton, grows each structure, runs every optimization, verifies the result, renders a garment proof, saves editable source, and exports PES.
; Keep the long generation job responsive and explain each milestone.
(jobs/progress 0.10 "Growing left wing")
(user/generate-wing design :left skeleton)
(yield)
; Finish with editable source, production facts, and a machine file.
(embroidery/save-design design
{:path "recursive-firebird-5x7.dither-emb"})
(embroidery/export design
{:format :pes
:path "recursive-firebird-5x7.pes"})
yield hands time back between bounded stages. Progress messages tell the user what the job is doing. The undo group makes the complete generation one reversible project action.
21. Generate a related variation
The variation command accepts a new seed, temporarily replaces only :seed inside user/firebird-art, and calls the main generator. The production envelope, palette, route, physics, and verification remain unchanged.
That is the payoff for separating controls from algorithms: one recipe can produce a coherent family without relaxing the rules that make each result sewable.
Close the module
The final provide form confirms that the source has finished defining the module named at the top.
; Mark the Recursive Firebird module as completely defined.
(provide dither://package/user/recursive-firebird)
You now have three useful reading levels: the beginner basics file, this guided tour, and the complete two-part source. Change one control at a time, keep the seed in your notes, and still make a physical test stitch on the intended garment stack.
Where to go next
- Browse the generated command reference.
- Keep comments beside every non-obvious choice.
- Turn one repeated calculation into a helper.
- Try one temporary variation before changing a favorite setting.
- Read the command and Help guide.