Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,21 @@ jobs:
# commit the push carried at once. That SHA is all zeros on a
# branch's first push and may be gone after a force push, so it
# is only used when the clone actually has it.
#
# The base is fetched at full depth, not --depth=1. A shallow
# fetch writes .git/shallow and grafts the branch one commit
# deep, and because the base shares its history with this one
# that truncates the whole clone: git log -- <file> then reports
# the graft point rather than the commit that last touched the
# file, which is what the sitemap-dates check reads. It failed
# on every page at once the first time a pull request ran after
# that check was added, naming today's date for files nobody had
# touched. fetch-depth: 0 above already pays for the history, so
# there is nothing to save here.
run: |
BASE=""
if [ -n "${{ github.base_ref }}" ]; then
git fetch --no-tags --depth=1 origin "${{ github.base_ref }}"
git fetch --no-tags origin "${{ github.base_ref }}"
BASE="origin/${{ github.base_ref }}"
elif git cat-file -e "${{ github.event.before }}^{commit}" 2>/dev/null; then
BASE="${{ github.event.before }}"
Expand Down
68 changes: 47 additions & 21 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@
files (not the vendored third-party ones, which are pinned and
don't change). Bump it whenever style.css or generator.js changes,
or a returning visitor's cached copy silently goes stale. -->
<link rel="stylesheet" href="style.css?v=108" />
<link rel="stylesheet" href="style.css?v=109" />
<!-- generator.js is requested at the end of the body, which is where
it has to run, but the preload scanner only finds it once that
end of a 200KB document has arrived. This starts the fetch
Expand Down Expand Up @@ -560,14 +560,18 @@ <h2 class="section-title">Settings</h2>
as a heading, and it is one of the four controls that drive
animation (see AGENTS.md) rather than a canvas setting. Second
in the tab, not first: size, colors and mask come before how
it moves, a still is the default, and the flash warning below
should not be the first line a new visitor reads. -->
it moves, a still is the default, and the flash warning here
should not be the first line a new visitor reads.

The warning is a sentence in the description rather than a
paragraph of its own: the section was four lines tall before
a single control, and a notice that takes that much room gets
read past as chrome. -->
<h2 class="section-title">Animation</h2>

<div class="field">
<label for="motion">Motion</label>
<p class="hint">Plays on the canvas as soon as you pick one.</p>
<p class="hint hint-warn">Motion flashes the whole canvas. Skip it if flashing affects you.</p>
<p class="hint">Plays as soon as you pick one. Skip it if flashing affects you.</p>
<select id="motion">
<option value="none" selected>None (still)</option>
<option value="circle">Loop</option>
Expand All @@ -592,13 +596,23 @@ <h2 class="section-title">Animation</h2>
large-area change in brightness, once a cycle. WCAG 2.3.1
allows three of those a second. A one second cycle is one.

Opens at three, not the two it used to, which is the length
every example clip in examples/ was rendered at: two reads
as hurried on Loop and Wind, where a whole turn of the
field goes by before the eye has followed it round. The
ladder is eight steps to a doubling, so three seconds
exactly is not a stop and 3.1s is the nearest one. -->
<input type="range" id="speed" min="16" max="48" value="29" />
Opens at thirteen and a half, well up from the three it
used to and the two the example clips in examples/ were
rendered at. A background is looked past rather than at,
and at three seconds a whole turn of the field goes by
inside a glance, which reads as something demanding
attention rather than something behind the work. Slow
enough and the canvas is never seen to move, only to have
moved. The ladder is eight steps to a doubling, so step 46
is 13454ms, which the panel rounds to 13.5s.

This is long enough to matter to a GIF. One pass is 168
frames at the 80ms the preview draws at, and the byte
budget below affords 69 on the default canvas, so an
export is slow motion at about 5fps. That reads fine at
this cycle length and is what GIF_FRAME_BUDGET's comment
anticipates; it is not a sign the budget is wrong. -->
<input type="range" id="speed" min="16" max="48" value="46" />
</div>

<!-- Depth scales how far every motion but Drift goes, and Direction
Expand Down Expand Up @@ -643,8 +657,14 @@ <h2 class="section-title">Auto-randomize</h2>
Both sliders start at a second now: Speed because of the
flash rate (see its own comment), this one because you
cannot read a composition thrown away four times a second
either. Stepped is the difference between them. -->
<input type="range" id="interval" min="16" max="48" step="8" value="24" />
either. Stepped is the difference between them.

Opens at four, not the two it used to. Two seconds is
about as long as it takes to notice what a composition is
doing, so rerolling on it means never seeing one settle;
four gives each one a beat to be looked at before the next
replaces it. Step 32 is exactly 4000ms. -->
<input type="range" id="interval" min="16" max="48" step="8" value="32" />
</div>

<h2 class="section-title">Saved states</h2>
Expand Down Expand Up @@ -892,7 +912,7 @@ <h2 class="section-title export-title">Export &amp; share</h2>

It stops at three. Five and eight were here, and under
Motion they are what took an export to 1.9GB of retained
frames on the Web preset: the budget now trims that, but
frames at 1080p: the budget now trims that, but
what it trims is the frames within each pass, so both
settings bought more passes by making every one of them
too coarse to read as motion. They cost nothing with
Expand Down Expand Up @@ -2056,7 +2076,7 @@ <h1 class="brand"><a href="./">Flield<span class="sr-only">: seamless animated b
// frame is width x height x 4 bytes and an export retains all of
// them at once, before the encoder has allocated anything of its
// own. Measured on the default 1200x630 canvas that is 2.9MB a
// frame, so the 240 above is 700MB; on the Web preset's 1920x1080
// frame, so the 240 above is 700MB; on a hand-typed 1920x1080
// it is 7.9MB a frame and 1.9GB, which is past what a phone's tab
// survives and enough to hang a desktop one. Canvas size is a free
// text field, so there is no size this can assume. Whichever of the
Expand Down Expand Up @@ -2386,9 +2406,15 @@ <h1 class="brand"><a href="./">Flield<span class="sr-only">: seamless animated b
render();
}

// Web and Tablet are real, recognizable resolutions (1080p; iPad
// portrait) that also happen to divide evenly by most of the block
// size range. Mobile's old 375x812 (iPhone X) was both a narrower,
// Web is the 1200x630 the document already opens at, so the chip is
// lit on arrival rather than offering a size nothing starts on. It
// is a wide canvas to work from, not a screen to fill: the
// width field is free text, so anyone wanting 1080p or wider types
// it, and nobody wanting a card-sized background has to shrink one.
// Tablet is a real, recognizable resolution (iPad portrait). Both
// divide evenly by most of the block size range.
//
// Mobile's old 375x812 (iPhone X) was both a narrower,
// dated reference and the one preset that couldn't tile evenly at
// any block size: 375 is odd, so no even block size divides it, and
// 812 only shares factors with a handful of block sizes (2, 4, 7,
Expand All @@ -2405,7 +2431,7 @@ <h1 class="brand"><a href="./">Flield<span class="sr-only">: seamless animated b
// so Tile forces it off. 512 is a nice round, highly divisible size
// for a texture swatch, not a specific device or screen to match.
const DIMENSION_PRESETS = {
web: { width: 1920, height: 1080 },
web: { width: 1200, height: 630 },
tablet: { width: 768, height: 1024 },
mobile: { width: 400, height: 800 },
tile: { width: 512, height: 512, shapeMask: "none", symmetry: "quad" },
Expand Down Expand Up @@ -2440,7 +2466,7 @@ <h1 class="brand"><a href="./">Flield<span class="sr-only">: seamless animated b

// Lights a preset chip while the current values still match it,
// derived from state rather than from the click: Web is lit however
// the size came to be 1920x1080, Tile only while the size is square
// the size came to be 1200x630, Tile only while the size is square
// with no mask and both layers on 4-way mirror, and a palette chip
// only while all six of its ranges still hold. Nudge anything and
// the chip goes quiet on its own.
Expand Down
31 changes: 0 additions & 31 deletions style.css
Original file line number Diff line number Diff line change
Expand Up @@ -2065,37 +2065,6 @@ button.secondary:hover:not(:disabled) {
margin-bottom: var(--space-2);
}

/* Two descriptions are one block, not two. */
.field .hint + .hint {
margin-top: calc(var(--space-1) - var(--space-2));
}

/* One hint carries a health warning rather than an explanation. It reads
in the same colour as the description above it, because a notice that
shouts gets taken for chrome and read past. A hanging marker carries
the difference instead, the way a footnote does. */
.hint-warn {
padding-left: 13px;
text-indent: -13px;
}

/* An asterisk is drawn small and parked up near the ascender, so at the
text's own size it reads as a footnote mark rather than as part of the
line. 1.54em makes its ink exactly the x-height of the 12px text, and
the negative vertical-align drops it from its own baseline onto the
text's, so it fills the same band the lowercase letters do. The zero
line-height keeps the bigger glyph from opening the line box up.
5px plus the glyph's advance is the 13px the hang is indented by, so
a second line starts under the first word rather than under the mark. */
.hint-warn::before {
content: "*";
font-size: 1.54em;
line-height: 0;
vertical-align: -0.356em;
padding-right: 5px;
color: var(--text);
}

/* Not in the strip above the bar on a phone: that row exists to put one
control within reach of the artwork, and a paragraph in it costs a
line of the picture for something already read in the sheet. */
Expand Down
Loading