genart.dev
Download GenArt
Wind in the Barley

Wind in the Barley

Engraved · 2026 · 1400×1000 · Canvas 2D

A field of barley under a moving wind, seen from a rise at its edge. The subject is the wind, which cannot be drawn. What can be drawn is what the wind does to a crop: where a gust presses the barley over, the stalks turn their sides up and the ears stop facing the viewer, and that patch goes pale; where the crop still stands, the eye looks down into it and it goes dark. So the gusts are pale sweeps running across a dark field, and their shape is the shape of the air. One pair of wheelings runs to the vanishing point — the only straight thing in the plate, and the only evidence of a person. The same two rules govern every mark here as govern the quarry. The direction of a hatch is the lean of the stalks it describes, so it reports the wind and nothing else, and the marks bend together where a gust crosses them. The density of a hatch is how far that crop is turned from the light. The one place in the plate where direction is not reporting the lean of something is the hedge, where the marks are thrown wide, and that scatter is the difference between a hedge and a wall. The field is drawn nearly flat in value from near to far. Distance is carried by the wheelings converging, by the gust bands foreshortening, and by the marks fining down — not by darkening the foreground, which turns level ground into a bank rising toward the viewer.

Technique

value is built the way an engraver builds it, by returning to the plate rather than by pressing harder. Four `plugin-painting` flow-line passes cover progressively smaller areas as the tone threshold rises, each set a little off the angle of the last, so the standing crop deepens by crossing. The first pass covers all of the crop and is never withheld — barley flattened by a gust is paler, not absent — and every boundary above it is dithered so a pass frays out instead of ending on a contour, which is what separates wind moving over a crop from ploughed ridges. Further layers draw the ears, the foreground stalks, the wheelings and the hedge. The ground is `plugin-textures` rough paper, the impression is a `plugin-shapes` rule, and `plugin-filters` grain carries the plate tone. The sketch itself draws nothing: it computes the ground plane, the gust field, a tone map and a stalk-direction map from the seed and publishes them on the ADR 062 data bridge, and every line in the picture is made by a layer reading those channels. Changing the seed moves the horizon, the vanishing point, the wheelings, the tree and the whole weather.

Seeds

The same system at three seeds. The composition itself re-cuts: the geometry is derived from the seed, so masses, edges and placement all move, while the palette and the drawing language stay put.

  1. Wind in the Barley, seed 22872287
  2. Wind in the Barley, seed 91059105
  3. Wind in the Barley, seed 913913

Layer stack

In paint order. Every mark in the image comes from these; there is no handwritten drawing code.

  1. textures:paperSheet
  2. painting:flow-linesHedge — Field Boundary
  3. painting:flow-linesHedge — Base and Ditch
  4. painting:flow-linesField Pass 1 — Standing Crop
  5. painting:flow-linesField Pass 2 — Half Tone
  6. painting:flow-linesField Pass 3 — Shadow
  7. painting:flow-linesField Pass 4 — Cross, Deepest Only
  8. painting:flow-linesEars — Broken Texture
  9. painting:flow-linesWheelings — Tramline
  10. painting:flow-linesNear Crop — Foreground Stalks
  11. shapes:rectPlate Mark
  12. filter:grainPlate Tone

Source

The complete composition. Open it in GenArt to re-render, re-seed, or take it apart.

wind-in-the-barley.genart
{
  "genart": "1.2",
  "id": "wind-in-the-barley",
  "title": "Wind in the Barley",
  "created": "2026-09-10T00:00:00Z",
  "modified": "2026-09-10T19:31:32.540Z",
  "renderer": {
    "type": "canvas2d",
    "version": "1.x"
  },
  "canvas": {
    "width": 1400,
    "height": 1000
  },
  "parameters": [],
  "colors": [],
  "dataChannels": [
    {
      "name": "crop1",
      "type": "vector",
      "cols": 400,
      "rows": 300
    },
    {
      "name": "crop2",
      "type": "vector",
      "cols": 400,
      "rows": 300
    },
    {
      "name": "crop3",
      "type": "vector",
      "cols": 400,
      "rows": 300
    },
    {
      "name": "crop4",
      "type": "vector",
      "cols": 400,
      "rows": 300
    },
    {
      "name": "ears",
      "type": "vector",
      "cols": 400,
      "rows": 300
    },
    {
      "name": "near",
      "type": "vector",
      "cols": 400,
      "rows": 300
    },
    {
      "name": "hedge",
      "type": "vector",
      "cols": 400,
      "rows": 300
    },
    {
      "name": "hedge-base",
      "type": "vector",
      "cols": 400,
      "rows": 300
    },
    {
      "name": "rut",
      "type": "vector",
      "cols": 400,
      "rows": 300
    }
  ],
  "state": {
    "seed": 2287,
    "params": {},
    "colorPalette": [
      "#2b1a12",
      "#5c3320",
      "#8a5a3c",
      "#c9a982",
      "#ece3d4"
    ]
  },
  "algorithm": "// Wind in the Barley — field generation only. This code makes NO marks.\n// It publishes a tone map and a stalk-direction map on the ADR 062 data\n// bridge; every line in the picture is drawn by a plugin layer that reads them.\n//\n// Two rules govern the plate, the same two that govern Quarry Face:\n//   the direction of a hatch = the lean of the stalks it describes\n//   the density of a hatch   = how far that crop is turned from the light\n//\n// The subject is the wind, which cannot be drawn. What can be drawn is what\n// the wind does to a crop: where a gust presses the barley over, the stalks\n// turn their sides up and the ears stop facing the viewer, and that patch goes\n// pale. Where the crop still stands, the eye looks down into it and it goes\n// dark. So the gusts are pale streaks running across a dark field, and their\n// shape is the shape of the air.\n//\n// Everything is computed on the ground plane and projected, so the gust bands\n// and the tramlines foreshorten and converge on one vanishing point instead of\n// being drawn as screen-space decoration.\nfunction sketch(ctx, state) {\n  var W = state.canvas.width, H = state.canvas.height;\n  var seed = state.seed || 0;\n\n  ctx.fillStyle = state.colorPalette[4];\n  ctx.fillRect(0, 0, W, H);\n\n  // The field grid scales with the canvas so a cell stays the same size in\n  // pixels. Holding it fixed softens every mask edge as the plate grows.\n  var COLS = Math.round(200 * W / 700), ROWS = Math.round(150 * H / 500);\n\n  function rngFrom(a) {\n    return function () {\n      a |= 0; a = a + 0x6D2B79F5 | 0;\n      var t = Math.imul(a ^ a >>> 15, 1 | a);\n      t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;\n      return ((t ^ t >>> 14) >>> 0) / 4294967296;\n    };\n  }\n  var rand = rngFrom(seed * 2654435761 + 12345);\n  var ph = [];\n  for (var i = 0; i < 24; i++) ph.push(rand() * Math.PI * 2);\n\n  /** A lattice of random values, bilinearly sampled. Used only to give the\n   *  crop its crookedness: a barley stalk is not a ruled line, and a field of\n   *  ruled lines is the one thing that would give the whole plate away. */\n  function lattice(nx, ny) {\n    var v = new Float32Array(nx * ny);\n    for (var k = 0; k < nx * ny; k++) v[k] = rand() * 2 - 1;\n    return function (u, t) {\n      // 🔴 Clamp. A sample outside [0,1] indexes past the lattice, and the\n      // undefined it returns becomes a NaN direction in a published channel.\n      // The flow-line stepper then walks to a NaN position, whose bounds check\n      // passes (every comparison with NaN is false), and dies indexing\n      // samples[NaN] — reported as \"Cannot read properties of undefined\n      // (reading 'dx')\" on a frame the CLI still exits 0 for.\n      if (u < 0) u = 0; else if (u > 1) u = 1;\n      if (t < 0) t = 0; else if (t > 1) t = 1;\n      var x = u * (nx - 1), y = t * (ny - 1);\n      var x0 = Math.floor(x), y0 = Math.floor(y);\n      var x1 = x0 + 1 > nx - 1 ? nx - 1 : x0 + 1, y1 = y0 + 1 > ny - 1 ? ny - 1 : y0 + 1;\n      var fx = x - x0, fy = y - y0;\n      fx = fx * fx * (3 - 2 * fx); fy = fy * fy * (3 - 2 * fy);\n      var a = v[y0 * nx + x0], b = v[y0 * nx + x1], c = v[y1 * nx + x0], d = v[y1 * nx + x1];\n      return (a + (b - a) * fx) * (1 - fy) + (c + (d - c) * fx) * fy;\n    };\n  }\n  // Every lattice is indexed in [0,1], so its features are a fixed fraction of\n  // the plate and scale with it. Resolutions are therefore literals, and the\n  // seeded picture is the same picture at any canvas size.\n  var clump = lattice(17, 13);   // stalks lean together in patches\n  var splay = lattice(61, 47);   // and the awns splay inside a patch\n  var edge  = lattice(53, 41);   // used to break binary mask boundaries\n  var nod   = lattice(97, 73);   // how each ear nods off its own stalk\n  // 🔴 The one that stops the field reading as combed fur. A smooth jitter\n  // coarser than the spacing between lines bends every neighbour by the same\n  // amount, so they stay parallel and the crop turns into a pelt. This one is\n  // finer than the line spacing, so adjacent stalks genuinely disagree.\n  var kink  = lattice(151, 113);\n  // One dither lattice per plate pass, at four different resolutions. A pass\n  // covers a hard-edged region and a hard edge shows as a contour drawn across\n  // the crop, so each threshold is jogged cell by cell and its boundary breaks\n  // into stipple. They have to be four DIFFERENT lattices: share one and the\n  // four boundaries jog together, land on top of each other and resolve back\n  // into the single hard contour the dither was there to destroy.\n  var dith  = [lattice(83, 63), lattice(79, 59), lattice(89, 67), lattice(73, 55)];\n\n  // --- The ground -------------------------------------------------------\n  var hv     = 0.150 + rand() * 0.048;   // the skyline\n  // Kept off centre. A vanishing point in the middle with a tramline pair\n  // straddling it turns the plate into a symmetrical funnel.\n  var vpU    = (rand() < 0.5 ? 0.15 + rand() * 0.18 : 0.67 + rand() * 0.18);\n  var XK     = 1.15;                     // ground units visible per unit depth\n  // 🔴 How far back the viewer stands, in the same units as a gust wavelength.\n  // At 1 the near edge of the plate is right at the viewer's feet, and because\n  // perspective makes the visible width proportional to the distance, the whole\n  // foreground then falls inside a third of one gust and can only be a single\n  // flat mass however the tone is graded. Standing back puts more of the plate\n  // at the distances where the wind is legible, which is why a landscape is\n  // almost never drawn from the ground at one's own feet.\n  var DNEAR  = 2.6;\n  var DMAX   = DNEAR * 24;\n  // A gust band is a line of constant phase on the ground. At windTh = PI/2 it\n  // runs directly away from the viewer and converges at the vanishing point;\n  // at 0 it lies flat across the view as a horizontal stripe, which reads as\n  // depth banding rather than as weather. So it stays near PI/2, tilted just\n  // far enough that the gusts cross the tramlines at a shallow angle.\n  var windTh = 1.15 + rand() * 0.34;\n  var wSin = Math.sin(windTh), wCos = Math.cos(windTh);\n  // Which way the crop is pushed on the sheet. One direction only: the wind\n  // does not change its mind inside a single plate.\n  var leanSign = rand() < 0.5 ? -1 : 1;\n\n  // The tramline: one pair of wheelings, laid by a machine, running to the\n  // vanishing point. It is the only straight thing in the picture and the only\n  // evidence of a person.\n  // In ground units, so they hold their real width however far back the\n  // viewer stands.\n  var tramX  = (rand() * 2 - 1) * 0.55 * DNEAR;\n  var gauge  = (0.26 + rand() * 0.09) * DNEAR;\n  var rutW   = (0.019 + rand() * 0.009) * DNEAR;\n\n  // The hedge that closes the field. Its top edge is the only drawing in the\n  // upper third, so it carries the whole horizontal, and it is not a strip of\n  // even height. It is bushed up in places, thin in\n  // others, and somewhere along it one thing has been let grow into a tree —\n  // which is the only vertical accent in the upper third and the thing that\n  // gives the horizon a scale.\n  var treeU = 0.13 + rand() * 0.74;\n  function hedgeTop(u) {\n    var body = 0.026\n             + 0.024 * (0.5 + 0.5 * Math.sin(u * 2.3 + ph[0]))\n             + 0.017 * (0.5 + 0.5 * Math.sin(u * 5.9 + ph[1]))\n             + 0.010 * (0.5 + 0.5 * Math.sin(u * 17.1 + ph[6]))\n             + 0.006 * (0.5 + 0.5 * Math.sin(u * 41.3 + ph[9]));\n    // One tree, not two, and tall and narrow rather than broad: a pair of\n    // broad ones sat on the horizon as a pair of hills. Built from three\n    // overlapping lobes, because a single bell curve is a cone and reads as a\n    // haystack.\n    var tree = 0.098 * Math.exp(-Math.pow((u - treeU) * 30.0, 2))\n             + 0.062 * Math.exp(-Math.pow((u - treeU + 0.021) * 41.0, 2))\n             + 0.052 * Math.exp(-Math.pow((u - treeU - 0.026) * 36.0, 2));\n    return hv - (body + tree);\n  }\n\n  var tone = new Float32Array(COLS * ROWS);\n  var dir = new Float32Array(COLS * ROWS);\n  var nearMask = new Float32Array(COLS * ROWS);\n  var hedgeTone = new Float32Array(COLS * ROWS);\n  var hedgeDir = new Float32Array(COLS * ROWS);\n  var hedgeBase = new Float32Array(COLS * ROWS);\n  var rutTone = new Float32Array(COLS * ROWS);\n  var rutDir = new Float32Array(COLS * ROWS);\n\n  for (var r2 = 0; r2 < ROWS; r2++) {\n    for (var c = 0; c < COLS; c++) {\n      var u = c / (COLS - 1), v = r2 / (ROWS - 1);\n      var idx = r2 * COLS + c;\n\n      var ht = hedgeTop(u);\n      if (v < ht) continue;                          // sky: bare paper\n\n      var HB = hv + 0.012;                           // the foot of the hedge\n      if (v < HB) {\n        // The hedge is solid at its base and open at the top, where the last\n        // growth stands separately against the sky. Filled to a hard edge it\n        // reads as a dark strip laid across the plate — a bar, not a hedge —\n        // so the crown thins out and only the body below is continuous. The\n        // break-up runs across three scales at once: one coarse lattice alone\n        // decides the whole width of the tree with a single value and fills it\n        // in as a solid mound, and the finer ones cut that mound into branches.\n        var f = (v - ht) / Math.max(0.004, HB - ht);\n        var solid = (f - 0.02) / 0.30; if (solid > 1) solid = 1; else if (solid < 0) solid = 0;\n        if (0.5 + 0.16 * clump(u, f) + 0.15 * splay(u, f) + 0.11 * nod(u, f) <= solid) {\n          // Foliage has no single direction, so its marks are thrown wide. That\n          // scatter is the difference between a hedge and a wall, and it is the\n          // only place in the plate where direction is not reporting the lean\n          // of something.\n          hedgeTone[idx] = 1;\n          hedgeDir[idx] = -Math.PI / 2 + 1.30 * kink(u, v) + 0.70 * splay(u, v) + 0.40 * clump(u, v);\n          // The base of a hedge, in its own shadow over the ditch, is the\n          // darkest thing on the horizon. It gets a second covering for the\n          // same reason the crop's troughs do: tone is built by returning.\n          if (f > 0.48) hedgeBase[idx] = 1;\n        }\n      }\n\n      // Project the cell onto the ground plane. 🔴 This runs UNDER the hedge as\n      // well as below it. Two binary masks that merely ABUT do not meet: the\n      // renderer interpolates the field between grid cells, so along the shared\n      // row both fall under the magnitude gate and neither draws, leaving a\n      // bright hairline across the full width of the plate. Masks that touch\n      // have to overlap.\n      var rec = (v - hv) / (1 - hv);                 // 1 at the near edge, 0 at the skyline\n      if (rec < DNEAR / DMAX) rec = DNEAR / DMAX;    // at and above the skyline, as far as it goes\n      var d = DNEAR / rec;                           // depth, DNEAR at the near edge\n      var gx = (u - vpU) * d * XK;\n\n      // The gust field, in ground coordinates so it foreshortens correctly.\n      // A gust does not travel as a ruled stripe. Its front snakes, and it\n      // breaks into patches along its own length, so the band is modulated by\n      // the coordinate that runs across it as well as the one along it.\n      var s = gx * wSin + d * wCos + 0.62 * Math.sin(d * 0.40 + ph[7]);\n      var sPerp = gx * wCos - d * wSin;\n      // Detail dissolves with distance. Without this the high-frequency terms\n      // oscillate faster than the field grid can carry them near the skyline\n      // and the far crop breaks into a moire that reads as corduroy.\n      var hf = 1 / (1 + d * 0.16 / DNEAR);\n      // Weighted so the broad sweep wins. Run the other way round, with the\n      // gusts louder than the sweep, the plate fills with evenly sized stripes\n      // and reads as corduroy: a texture, not weather. One large movement with\n      // smaller ones inside it is both what wind on a crop looks like and the\n      // only version of this that composes.\n      var g = Math.sin(s * 1.45 + ph[3]) * 0.95         // the one broad sweep\n            + Math.sin(s * 4.40 + ph[4]) * 0.52 * (0.35 + 0.65 * hf)   // the gusts inside it\n            + Math.sin(s * 9.80 + ph[5]) * 0.26 * hf;   // and their fraying\n      g /= 1.55;\n      g *= 0.74 + 0.26 * Math.sin(sPerp * 0.72 + ph[8]);\n      // You cannot make out one gust from another at the far side of a field,\n      // and trying to draw them there squeezes several cycles into the few\n      // grid rows left above the recession, which resolves as hard pale\n      // rectangles rather than as weather. So the figure fades out into an\n      // even grey before it reaches the hedge.\n      g *= 0.68 + 0.32 * hf;\n      // Pushed a little toward its limits so the plate has a settled range of\n      // value whatever the seed. Left raw, the sum of sines rarely approaches\n      // either end and the deepest pass finds almost nothing to cover: measured\n      // at three seeds, under half a percent of the plate reached the fourth\n      // threshold and the darks varied by a tenth between them. Pushed hard,\n      // though, the figure goes bimodal, its edges harden into ridge lines and\n      // the crop reads as dunes — so this is deliberately gentle.\n      g = Math.tanh(g * 1.55);\n\n      var pale = Math.pow(g > 0 ? g : 0, 1.30);        // crests: crop pressed over, turned from the light\n      var trough = Math.pow(g < 0 ? -g : 0, 1.25);     // troughs: crop standing, looked into\n\n      // Tone. The field itself is a middle value that only leans darker toward\n      // the viewer, because the eye looks down into near crop and the far crop\n      // closes up into one surface. The large swings belong to the wind: the\n      // deepest darks are standing crop in the troughs and the paper is left\n      // almost bare on the crests. Grading it the other way round, with the\n      // recession carrying the range, produced a plate that was a black field\n      // with weather in it somewhere.\n      // Depth is carried by the SIZE of the marks, which the layers' own depth\n      // scaling handles, so the tone map stays nearly flat from near to far and\n      // spends its range on the wind instead. Grading it steeply with distance\n      // made a plate that read as a dark bank rising out of a bright one.\n      var T = 0.30 + 0.05 * Math.sqrt(rec);\n      T -= 0.30 * pale;\n      T += 0.32 * trough;\n\n      // Lean. Strongest on the crest of a gust, never reversed.\n      var lean = leanSign * (0.06 + 0.52 * (0.5 + 0.5 * g));\n      var A = -Math.PI / 2 + lean + 0.15 * clump(u, v) + 0.13 * splay(u, v) + 0.23 * kink(u, v);\n\n      // The tramline. Bare soil in the wheelings, and the crop at their edges\n      // leans into the gap and stands against the light, so each rut is a pale\n      // line with a dark lip — which is exactly how you see one from the gate.\n      for (var t2 = -1; t2 <= 1; t2 += 2) {\n        var dr = Math.abs(gx - (tramX + t2 * gauge));\n        if (dr < rutW) {\n          T = 0;\n          // Wheelings and their ruts run to the vanishing point, so their few\n          // marks are the only ones in the crop that lie down in perspective.\n          rutTone[idx] = 1;\n          var du = u - vpU, dv = v - hv;\n          rutDir[idx] = Math.atan2(dv, du) + (du < 0 ? Math.PI : 0);\n        } else if (dr < rutW * 2.2) {\n          var f = 1 - (dr - rutW) / (rutW * 1.2);\n          T += 0.32 * f * f;\n        }\n      }\n\n      // 🔴 A floor, and it matters more than any other number here. Barley\n      // pressed flat by a gust is PALER, not absent: the stalks are still\n      // standing in it, they are just showing less of their own shadow. Let the\n      // crests fall through the first pass and they become bare paper, and a\n      // large ragged patch of bare paper in the middle of a field does not read\n      // as wind, it reads as a hole. So the first pass covers all of the crop\n      // and the whole wind figure is made by which of the LATER passes return\n      // to a place. Only the wheelings, which really are bare, sit below this.\n      if (rutTone[idx] === 0 && T < 0.19) T = 0.19;\n      if (T < 0) T = 0; else if (T > 1) T = 1;\n      tone[idx] = T;\n      dir[idx] = A;\n\n      // The near crop, drawn again with taller strokes so the foreground has\n      // its own scale. A straight horizontal cut would show as a line across\n      // the plate, so the boundary is broken by the same lattice that bends\n      // the stalks.\n      if (rec > 0.44 + 0.13 * edge(u, v) && rutTone[idx] === 0 && T >= 0.22) nearMask[idx] = 1;\n    }\n  }\n\n  // --- Publish one field per plate pass ---------------------------------\n  // An engraver builds value by returning to the plate, not by pressing\n  // harder. Each pass covers only what is at least as dark as its threshold,\n  // and sits a little off the last, so tone accumulates by crossing.\n  // Set against the measured tone distribution, not by eye. The first is high\n  // enough that the crests of the gusts fall below it and stay bare paper,\n  // which is the whole of how the wind reads; the last is low enough that the\n  // deepest crossing actually finds something to cover at every seed.\n  var THRESHOLDS = [0.14, 0.30, 0.42, 0.53];\n  // Wider than a quarry wall's, because barley genuinely splays: a bed of\n  // stalks all within a few degrees of each other combs into a pelt, and the\n  // crossing is what makes the dark read as ink rather than as shading.\n  var SKEW = [0.10, -0.24, 0.32, -0.55];\n  var g2 = (typeof globalThis !== 'undefined') ? globalThis : window;\n  g2.__genart_data = g2.__genart_data || {};\n  g2.__genart_data.cols = COLS;\n  g2.__genart_data.rows = ROWS;\n\n  function pack(name, mag, ang, skew) {\n    var f32 = new Float32Array(COLS * ROWS * 3);\n    for (var n = 0; n < COLS * ROWS; n++) {\n      var a = ang[n] + (skew || 0);\n      var m = mag[n];\n      // One non-finite cell anywhere in a channel takes the whole render down,\n      // so nothing unchecked is published. See the note in lattice().\n      if (!isFinite(a)) a = -Math.PI / 2;\n      if (!isFinite(m)) m = 0;\n      f32[n * 3] = Math.cos(a);\n      f32[n * 3 + 1] = Math.sin(a);\n      f32[n * 3 + 2] = m;\n    }\n    g2.__genart_data[name] = f32;\n  }\n\n  // The jog is wide — a fifth of the whole tonal range — and runs at two\n  // scales. This is the single thing that separates wind moving over a crop\n  // from ploughed ridges: a gust has no edge, it frays out over a stretch of\n  // ground several stalks deep, and a pass boundary cut at one tone contour\n  // instead reads as the lip of a dune however the value is graded.\n  for (var p = 0; p < THRESHOLDS.length; p++) {\n    var m = new Float32Array(COLS * ROWS);\n    for (var n2 = 0; n2 < COLS * ROWS; n2++) {\n      var uu = (n2 % COLS) / (COLS - 1), vv = Math.floor(n2 / COLS) / (ROWS - 1);\n      // The first pass is NOT jogged. It is the crop's own covering, it sits\n      // under the whole field, and dithering it punches pale holes right\n      // through the ground the other three are built on — which dapples the\n      // field like a leopard instead of fraying the edges between passes.\n      var th = p === 0 ? THRESHOLDS[0]\n             : THRESHOLDS[p] + 0.075 * dith[p](uu, vv) + 0.032 * clump(uu, vv);\n      m[n2] = tone[n2] >= th ? 1 : 0;\n    }\n    pack('crop' + (p + 1), m, dir, SKEW[p]);\n  }\n  // The ears are the one part of a barley plant that does not lie along the\n  // stalk: they nod off it at every angle, and their scatter is what stops a\n  // field reading as a pelt. Drawn very short and thrown wide of the lean.\n  var earsMask = new Float32Array(COLS * ROWS);\n  var earsDir = new Float32Array(COLS * ROWS);\n  for (var e = 0; e < COLS * ROWS; e++) {\n    earsMask[e] = tone[e] >= 0.26 ? 1 : 0;\n    earsDir[e] = dir[e] + 0.62 * nod((e % COLS) / (COLS - 1), Math.floor(e / COLS) / (ROWS - 1));\n  }\n  pack('ears', earsMask, earsDir, 0);\n  pack('near', nearMask, dir, 0);\n  pack('hedge', hedgeTone, hedgeDir, 0);\n  pack('hedge-base', hedgeBase, hedgeDir, -0.55);\n  pack('rut', rutTone, rutDir, 0);\n}\n",
  "layers": [
    {
      "id": "sheet",
      "type": "textures:paper",
      "name": "Sheet",
      "visible": true,
      "locked": false,
      "opacity": 1,
      "blendMode": "normal",
      "transform": {
        "x": 0,
        "y": 0,
        "width": 1400,
        "height": 1000,
        "rotation": 0,
        "scaleX": 1,
        "scaleY": 1,
        "anchorX": 0,
        "anchorY": 0
      },
      "properties": {
        "preset": "rough",
        "roughness": -1,
        "color": "#ece3d4",
        "seed": 2287
      }
    },
    {
      "id": "hedge",
      "type": "painting:flow-lines",
      "name": "Hedge — Field Boundary",
      "visible": true,
      "locked": false,
      "opacity": 1,
      "blendMode": "normal",
      "transform": {
        "x": 105,
        "y": 75,
        "width": 1190,
        "height": 810,
        "rotation": 0,
        "scaleX": 1,
        "scaleY": 1,
        "anchorX": 0,
        "anchorY": 0
      },
      "properties": {
        "field": "algorithm:hedge",
        "fieldCols": 400,
        "fieldRows": 300,
        "minMagnitude": 0.5,
        "seed": 2378,
        "lineCount": 34000,
        "seedDistribution": "grid-jittered",
        "lineLength": 7,
        "stepSize": 4,
        "lineWeight": 1.16,
        "lineWeightVariation": 0.34,
        "taper": "none",
        "color": "#2b1a12",
        "colorVariation": 0.07,
        "opacity": 0.36,
        "paintMode": "multiply",
        "depthScale": false,
        "horizonY": 0.18,
        "depthWeightRange": "[0.80, 1.08]",
        "depthOpacityRange": "[0.94, 1.00]",
        "maskCenterY": -1,
        "maskSpread": 0.25
      }
    },
    {
      "id": "hedge-base",
      "type": "painting:flow-lines",
      "name": "Hedge — Base and Ditch",
      "visible": true,
      "locked": false,
      "opacity": 1,
      "blendMode": "normal",
      "transform": {
        "x": 105,
        "y": 75,
        "width": 1190,
        "height": 810,
        "rotation": 0,
        "scaleX": 1,
        "scaleY": 1,
        "anchorX": 0,
        "anchorY": 0
      },
      "properties": {
        "field": "algorithm:hedge-base",
        "fieldCols": 400,
        "fieldRows": 300,
        "minMagnitude": 0.5,
        "seed": 2390,
        "lineCount": 24000,
        "seedDistribution": "grid-jittered",
        "lineLength": 6,
        "stepSize": 4,
        "lineWeight": 1.16,
        "lineWeightVariation": 0.34,
        "taper": "none",
        "color": "#2b1a12",
        "colorVariation": 0.07,
        "opacity": 0.38,
        "paintMode": "multiply",
        "depthScale": false,
        "horizonY": 0.18,
        "depthWeightRange": "[0.80, 1.08]",
        "depthOpacityRange": "[0.94, 1.00]",
        "maskCenterY": -1,
        "maskSpread": 0.25
      }
    },
    {
      "id": "pass-1",
      "type": "painting:flow-lines",
      "name": "Field Pass 1 — Standing Crop",
      "visible": true,
      "locked": false,
      "opacity": 1,
      "blendMode": "normal",
      "transform": {
        "x": 105,
        "y": 75,
        "width": 1190,
        "height": 810,
        "rotation": 0,
        "scaleX": 1,
        "scaleY": 1,
        "anchorX": 0,
        "anchorY": 0
      },
      "properties": {
        "field": "algorithm:crop1",
        "fieldCols": 400,
        "fieldRows": 300,
        "minMagnitude": 0.5,
        "seed": 2300,
        "lineCount": 30000,
        "seedDistribution": "grid-jittered",
        "lineLength": 14,
        "stepSize": 4,
        "lineWeight": 1.08,
        "lineWeightVariation": 0.14,
        "taper": "none",
        "color": "#2b1a12",
        "colorVariation": 0.05,
        "opacity": 0.26,
        "paintMode": "multiply",
        "depthScale": true,
        "horizonY": 0.18,
        "depthWeightRange": "[0.80, 1.08]",
        "depthOpacityRange": "[0.94, 1.00]",
        "maskCenterY": -1,
        "maskSpread": 0.25
      }
    },
    {
      "id": "pass-2",
      "type": "painting:flow-lines",
      "name": "Field Pass 2 — Half Tone",
      "visible": true,
      "locked": false,
      "opacity": 1,
      "blendMode": "normal",
      "transform": {
        "x": 105,
        "y": 75,
        "width": 1190,
        "height": 810,
        "rotation": 0,
        "scaleX": 1,
        "scaleY": 1,
        "anchorX": 0,
        "anchorY": 0
      },
      "properties": {
        "field": "algorithm:crop2",
        "fieldCols": 400,
        "fieldRows": 300,
        "minMagnitude": 0.5,
        "seed": 2313,
        "lineCount": 29000,
        "seedDistribution": "grid-jittered",
        "lineLength": 11,
        "stepSize": 4,
        "lineWeight": 1.1,
        "lineWeightVariation": 0.14,
        "taper": "none",
        "color": "#2b1a12",
        "colorVariation": 0.05,
        "opacity": 0.31,
        "paintMode": "multiply",
        "depthScale": true,
        "horizonY": 0.18,
        "depthWeightRange": "[0.80, 1.08]",
        "depthOpacityRange": "[0.94, 1.00]",
        "maskCenterY": -1,
        "maskSpread": 0.25
      }
    },
    {
      "id": "pass-3",
      "type": "painting:flow-lines",
      "name": "Field Pass 3 — Shadow",
      "visible": true,
      "locked": false,
      "opacity": 1,
      "blendMode": "normal",
      "transform": {
        "x": 105,
        "y": 75,
        "width": 1190,
        "height": 810,
        "rotation": 0,
        "scaleX": 1,
        "scaleY": 1,
        "anchorX": 0,
        "anchorY": 0
      },
      "properties": {
        "field": "algorithm:crop3",
        "fieldCols": 400,
        "fieldRows": 300,
        "minMagnitude": 0.5,
        "seed": 2326,
        "lineCount": 27000,
        "seedDistribution": "grid-jittered",
        "lineLength": 9,
        "stepSize": 4,
        "lineWeight": 1.2,
        "lineWeightVariation": 0.14,
        "taper": "none",
        "color": "#2b1a12",
        "colorVariation": 0.05,
        "opacity": 0.41,
        "paintMode": "multiply",
        "depthScale": true,
        "horizonY": 0.18,
        "depthWeightRange": "[0.80, 1.08]",
        "depthOpacityRange": "[0.94, 1.00]",
        "maskCenterY": -1,
        "maskSpread": 0.25
      }
    },
    {
      "id": "pass-4",
      "type": "painting:flow-lines",
      "name": "Field Pass 4 — Cross, Deepest Only",
      "visible": true,
      "locked": false,
      "opacity": 1,
      "blendMode": "normal",
      "transform": {
        "x": 105,
        "y": 75,
        "width": 1190,
        "height": 810,
        "rotation": 0,
        "scaleX": 1,
        "scaleY": 1,
        "anchorX": 0,
        "anchorY": 0
      },
      "properties": {
        "field": "algorithm:crop4",
        "fieldCols": 400,
        "fieldRows": 300,
        "minMagnitude": 0.5,
        "seed": 2339,
        "lineCount": 23000,
        "seedDistribution": "grid-jittered",
        "lineLength": 7,
        "stepSize": 4,
        "lineWeight": 1.28,
        "lineWeightVariation": 0.14,
        "taper": "none",
        "color": "#2b1a12",
        "colorVariation": 0.05,
        "opacity": 0.47,
        "paintMode": "multiply",
        "depthScale": true,
        "horizonY": 0.18,
        "depthWeightRange": "[0.80, 1.08]",
        "depthOpacityRange": "[0.94, 1.00]",
        "maskCenterY": -1,
        "maskSpread": 0.25
      }
    },
    {
      "id": "ears",
      "type": "painting:flow-lines",
      "name": "Ears — Broken Texture",
      "visible": true,
      "locked": false,
      "opacity": 1,
      "blendMode": "normal",
      "transform": {
        "x": 105,
        "y": 75,
        "width": 1190,
        "height": 810,
        "rotation": 0,
        "scaleX": 1,
        "scaleY": 1,
        "anchorX": 0,
        "anchorY": 0
      },
      "properties": {
        "field": "algorithm:ears",
        "fieldCols": 400,
        "fieldRows": 300,
        "minMagnitude": 0.5,
        "seed": 2498,
        "lineCount": 24000,
        "seedDistribution": "grid-jittered",
        "lineLength": 5,
        "stepSize": 4,
        "lineWeight": 1.12,
        "lineWeightVariation": 0.34,
        "taper": "none",
        "color": "#2b1a12",
        "colorVariation": 0.07,
        "opacity": 0.28,
        "paintMode": "multiply",
        "depthScale": true,
        "horizonY": 0.18,
        "depthWeightRange": "[0.80, 1.08]",
        "depthOpacityRange": "[0.94, 1.00]",
        "maskCenterY": -1,
        "maskSpread": 0.25
      }
    },
    {
      "id": "wheelings",
      "type": "painting:flow-lines",
      "name": "Wheelings — Tramline",
      "visible": true,
      "locked": false,
      "opacity": 1,
      "blendMode": "normal",
      "transform": {
        "x": 105,
        "y": 75,
        "width": 1190,
        "height": 810,
        "rotation": 0,
        "scaleX": 1,
        "scaleY": 1,
        "anchorX": 0,
        "anchorY": 0
      },
      "properties": {
        "field": "algorithm:rut",
        "fieldCols": 400,
        "fieldRows": 300,
        "minMagnitude": 0.5,
        "seed": 2344,
        "lineCount": 90000,
        "seedDistribution": "grid-jittered",
        "lineLength": 7,
        "stepSize": 4,
        "lineWeight": 1,
        "lineWeightVariation": 0.34,
        "taper": "none",
        "color": "#2b1a12",
        "colorVariation": 0.05,
        "opacity": 0.22,
        "paintMode": "multiply",
        "depthScale": true,
        "horizonY": 0.18,
        "depthWeightRange": "[0.70, 1.10]",
        "depthOpacityRange": "[0.94, 1.00]",
        "maskCenterY": -1,
        "maskSpread": 0.25
      }
    },
    {
      "id": "near-crop",
      "type": "painting:flow-lines",
      "name": "Near Crop — Foreground Stalks",
      "visible": true,
      "locked": false,
      "opacity": 1,
      "blendMode": "normal",
      "transform": {
        "x": 105,
        "y": 75,
        "width": 1190,
        "height": 810,
        "rotation": 0,
        "scaleX": 1,
        "scaleY": 1,
        "anchorX": 0,
        "anchorY": 0
      },
      "properties": {
        "field": "algorithm:near",
        "fieldCols": 400,
        "fieldRows": 300,
        "minMagnitude": 0.5,
        "seed": 2424,
        "lineCount": 6800,
        "seedDistribution": "grid-jittered",
        "lineLength": 16,
        "stepSize": 4,
        "lineWeight": 1.48,
        "lineWeightVariation": 0.14,
        "taper": "tail",
        "color": "#2b1a12",
        "colorVariation": 0.05,
        "opacity": 0.24,
        "paintMode": "multiply",
        "depthScale": true,
        "horizonY": 0.18,
        "depthWeightRange": "[0.96, 1.12]",
        "depthOpacityRange": "[0.94, 1.00]",
        "maskCenterY": -1,
        "maskSpread": 0.25
      }
    },
    {
      "id": "plate-mark",
      "type": "shapes:rect",
      "name": "Plate Mark",
      "visible": true,
      "locked": false,
      "opacity": 0.55,
      "blendMode": "normal",
      "transform": {
        "x": 105,
        "y": 75,
        "width": 1190,
        "height": 810,
        "rotation": 0,
        "scaleX": 1,
        "scaleY": 1,
        "anchorX": 0,
        "anchorY": 0
      },
      "properties": {
        "fillColor": "#ffffff",
        "fillEnabled": false,
        "strokeColor": "#8a5a3c",
        "strokeWidth": 2,
        "strokeEnabled": true,
        "cornerRadius": 0
      }
    },
    {
      "id": "tone",
      "type": "filter:grain",
      "name": "Plate Tone",
      "visible": true,
      "locked": false,
      "opacity": 1,
      "blendMode": "normal",
      "transform": {
        "x": 0,
        "y": 0,
        "width": 1400,
        "height": 1000,
        "rotation": 0,
        "scaleX": 1,
        "scaleY": 1,
        "anchorX": 0,
        "anchorY": 0
      },
      "properties": {
        "intensity": 0.1,
        "size": 2,
        "seed": 2287,
        "monochrome": true
      }
    }
  ]
}