Build Adobe Express

KINDAreplaces $9.99/mosaves $119.88/yrback to the verdict

0%0 of 17 items done

Saved on this device only. Tick prerequisites first, then work the phases in order · do not start one until the checks above it pass.

A template renderer whose hard problems are producing the same pixels every time despite fonts and images loading asynchronously, fitting real text into a fixed box, and keeping a licence record for every asset that ends up in an export.

estimated effort weekendthe files for this build are in the project pack

RenderingA headless browser screenshotting an HTML template at an exact pixel sizeTemplatesPlain HTML and CSS files reading values from one brand fileBrandOne file holding fonts, colours, logo and defaults, read by every templateCut-outsA local background removal model, chosen deliberately and recordedProvenanceA sidecar file per export listing fields, template version and asset licences

Before step 1

Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.

  1. installfree

    Why Runs the renderer and the batch command.

    Get it nodejs.org, or your version manager. open ↗

    Verify node --version prints v22 or higher

  2. installfree

    Why The render is a screenshot of a real page, which is how you get text layout that is correct rather than approximated.

    Get it Install the library and let it download its browser.

    Verify A blank page screenshots at an exact requested size

  3. installfree for the permissive models; a commercial agreement for some others

    Why The library is permissive but its models are not uniformly so, and the default one requires a paid agreement for commercial use.

    Get it Install the library, read its model list and its licence notes, then choose a model and record why. open ↗

    Verify The chosen model name and its licence are in configuration before any cut-out is produced

  4. have readyfree for open fonts; whatever the foundry charges otherwise

    Why Rendering is unrestricted under an open font licence but the file itself travels with conditions, and a client font may be neither.

    Get it Download the font files with their licence text and keep both in the repository.

    Verify Every font in the brand file has a licence recorded in the assets table

  5. decidefree

    Why Platform image specifications change and a size baked into a template is never revisited, so the date is part of the data.

    Get it Look up the current published specification for each platform you export to and record the dimensions with the date.

    Verify Every size in configuration carries a checked date

Data model

Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.

- templates(id, name, width, height, version, html_path, fields_json) · size is part of the identity, not a setting
- brand(fonts_json, colours_json, logo_path, licence_notes) · one record, read by every render
- assets(id, kind, path, sha256, source, licence_name, licence_url, commercial_ok, added_at) · kind is font, image or model
- renders(id, template_id, template_version, field_values_json, asset_ids_json, output_path, rendered_at, duration_ms)
- sidecars(render_id, json_path) · the file written beside every export so it can be reproduced
- The assets table is the one that stops this being a tool that quietly puts a non-commercial model's output on a billboard.

Environment variables

These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.

VariableNeededExampleWhere the value comes from
TEMPLATE_DIRrequired./templatesHTML and CSS templates. Each declares its own size.
BRAND_FILErequired./brand.jsonFonts, colours and logo. The only place these are defined.
OUTPUT_DIRrequired~/GraphicsExports and their sidecars, named predictably by date and template.
CUTOUT_MODELrequireda permissively licensed segmentation model nameNamed explicitly. The library default is not permitted here.
PROJECT_USErequiredcommercialCommercial or personal. A commercial project refuses non-commercial model weights.
DEVICE_PIXEL_RATIOrequired2Fixed, because it is part of what makes two renders identical.

The build, in order

  1. The same pixels every time

    Rendering a template twice with the same values produces identical files.

    1. The load event is not the same as ready to photograph.

      Files src/render.jstests/determinism.test.js

    2. Size is part of the template identity.

      Files src/render.js

    3. A silently wrong graphic is worse than no graphic.

      Files src/assets.js

    done when · tick each as it passes
    watch out
    • Screenshotting as soon as the page fires its load event catches the moment before the web font has been applied, so roughly one render in ten comes out in the fallback font with different line breaks, and it is never the one you look at. Wait for the font loading promise and for every image to report itself decoded, then screenshot.
  2. One brand file that every template reads

    Changing a colour or a font once changes every graphic on the next render.

    1. One source for colour and type, read by every template.

      Files src/brand.jstemplates/_base.css

    2. Drift starts with one exception nobody removed.

      Files tests/no-literals.test.js

    3. The old graphics are the ones that look wrong first.

      Files src/rerender.js

    done when · tick each as it passes
    watch out
    • Templates that carry their own colours drift within a month, because the third one was made by copying the second and then adjusted, and by the time anyone notices there are five nearly-identical blues and no way to find them. Have the render fail when a template declares a colour or font family directly, and keep the check in the suite.
  3. Real text in a fixed box

    A long headline and a short one both look deliberate.

    1. Guessing from character count breaks on the first unusual string.

      Files src/fit.jstests/fit.test.js

    2. Unreadably small is a failure, not a solution.

      Files templates/schema.json

    3. All three appear in real names.

      Files tests/text-cases.test.js

    done when · tick each as it passes
    watch out
    • Choosing a font size from the number of characters works until somebody types a word that cannot be broken or a string of emoji, and then the headline either overflows the canvas silently or shrinks to something unreadable, and the export still succeeds. Measure the rendered text, honour a minimum size, and fail the render with the field name when the text cannot fit.
  4. Cut-outs, and the licence that comes with them

    Background removal runs locally with a model you chose on purpose and recorded.

    1. The default is the one with the commercial restriction.

      Files config/models.jsonsrc/cutout.js

    2. You will be asked, possibly by a client's lawyer.

      Files src/assets.jssrc/sidecar.js

    3. Better a failed render than a licence problem.

      Files src/cutout.jstests/licence.test.js

    done when · tick each as it passes
    watch out
    • Installing the popular background removal library and calling it with defaults uses a model released for non-commercial use, so the first client logo you produce is built on weights you do not have a licence for, and nothing in the tooling tells you. Name the model in configuration, record its licence beside the output, and make the build refuse a model marked as non-commercial when the project is marked commercial.
what this build does not replace
after v1, if you want it

Need the files? The project pack on the verdict page hands your agent the whole brief · more design.