Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
cd37ff1
ci: run on every push, Node 24 from .nvmrc, current action majors
TMHSDigital Sep 25, 2026
5cda972
repo: the new-source form offers the video category
TMHSDigital Sep 26, 2026
cb7a2d3
site: one frontmatter parser, stricter checks, and a build that fails…
TMHSDigital Sep 26, 2026
6338ac7
dev: npm scripts, a new-entry scaffold, and one command for every count
TMHSDigital Sep 26, 2026
0c0233e
docs: the template, CONTRIBUTING and PR template match the validator
TMHSDigital Sep 26, 2026
e3fb1f9
catalog: remove the internal r04 tag
TMHSDigital Sep 26, 2026
1608bea
catalog: merge subcategory synonyms
TMHSDigital Sep 26, 2026
12ee83f
catalog: pick one spelling for duplicate tags
TMHSDigital Sep 26, 2026
2d3b348
catalog: normalize format names
TMHSDigital Sep 26, 2026
96e5373
catalog: set publisher on three two-entry groups
TMHSDigital Sep 26, 2026
9bcdb0b
catalog: point gdquest-3d-mannequin at its new repo
TMHSDigital Sep 26, 2026
3a0277c
checks: -es and -ies plurals, and retired spellings stay retired
TMHSDigital Sep 26, 2026
597961d
ci: weekly link check that opens one correction issue
TMHSDigital Sep 26, 2026
0b47c17
site: link the sitemap from the homepage
TMHSDigital Sep 26, 2026
6273387
catalog: point the two Blender bundles at the demo-files listing
TMHSDigital Sep 26, 2026
b596aa3
site: search matches word starts, not substrings
TMHSDigital Sep 26, 2026
f611f7f
site: filter by toggling kept rows, debounce search typing
TMHSDigital Sep 26, 2026
43ef613
site: keep keyboard focus when a filter chip is removed
TMHSDigital Sep 26, 2026
59d9763
site: nest card titles under category headings, say what the count means
TMHSDigital Sep 26, 2026
75aced4
site: chip counts at full contrast in both themes
TMHSDigital Sep 26, 2026
a9b4c4e
site: 44px tap targets for in-card links and filters on touch screens
TMHSDigital Sep 26, 2026
8544b2f
site: invalid URL params fall back to defaults
TMHSDigital Sep 26, 2026
c563120
site: defer the homepage scripts
TMHSDigital Sep 26, 2026
08afe5f
site: say visibly that "Commercial OK" keeps per-file sources
TMHSDigital Sep 26, 2026
31aae2a
site: fold the filter panel behind a disclosure on small screens
TMHSDigital Sep 26, 2026
cdba0b4
site: the entry breadcrumb returns to the reader's search and filters
TMHSDigital Sep 26, 2026
24a61b8
site: the 404 page suggests the closest entries
TMHSDigital Sep 26, 2026
38498f8
site: the prerendered result count reads like the live one
TMHSDigital Sep 26, 2026
7207f5e
catalog: notice-only licences no longer ask for an on-screen credit
TMHSDigital Sep 26, 2026
a42fb6b
catalog: fill 2D view and grid fields where the source states them
TMHSDigital Sep 26, 2026
55acbcc
docs: record the repo review and its fixes in the ledger
TMHSDigital Sep 26, 2026
0446265
Merge main (#48) into review-fixes
TMHSDigital Sep 26, 2026
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
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/new-source.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ body:
- characters
- audio
- animation
- video
- environment
- shaders-vfx
- fonts
Expand Down
11 changes: 7 additions & 4 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@

- [ ] New catalog entry
- [ ] License / metadata correction
- [ ] Starter stack
- [ ] Docs or site

## Checklist

- [ ] Copied `catalog/TEMPLATE.md` (new entries)
- [ ] Started from `node site/new-entry.mjs <category> <id>` or copied `catalog/TEMPLATE.md` (new entries)
- [ ] `id` is unique across the whole catalog
- [ ] Live license checked today; `active` entries have dated `## Evidence`
- [ ] Bumped `expectedEntryCount` in `site/config.json`
- [ ] `node site/validate.mjs` passes
- [ ] Live license checked today; `## Evidence` has a dated line quoting it
- [ ] Row added to the category `README.md`, with the same licence as the frontmatter
- [ ] Ran `node site/sync-counts.mjs` after adding or removing entries
- [ ] Stacks: followed `stacks/README.md`; no licence named in a stack's own words
- [ ] `npm run check` passes (tests, `node site/validate.mjs`, `node site/build.mjs`)
8 changes: 8 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: monthly
commit-message:
prefix: ci
18 changes: 9 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
name: Validate catalog

# No path filters: the build reads catalog/, stacks/, docs/, site/ and the
# top-level Markdown files, and the whole run takes seconds.
on:
pull_request:
push:
branches: [main]
paths:
- "catalog/**"
- "docs/**"
- "site/**"
- "README.md"
- ".github/workflows/ci.yml"
workflow_call:

permissions:
contents: read

jobs:
validate:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v7

- name: Setup Node
uses: actions/setup-node@v4
uses: actions/setup-node@v7
with:
node-version: "20"
node-version-file: .nvmrc

- name: Test catalog checks
run: node site/checks.test.mjs
Expand Down
44 changes: 44 additions & 0 deletions .github/workflows/links.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: Check source links

# Weekly: fetch every entry's url, and open or update one issue listing dead
# links, sources that moved, and entries whose licence check is over a year
# old. It never edits an entry: each one needs its licence re-read first.
on:
schedule:
- cron: "41 7 * * 1"
workflow_dispatch:

permissions:
contents: read
issues: write

jobs:
links:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7

- name: Setup Node
uses: actions/setup-node@v7
with:
node-version-file: .nvmrc

- name: Check links
id: check
run: node site/check-links.mjs --report link-report.md

- name: Open or update the report issue
if: steps.check.outputs.problems != '0'
env:
GH_TOKEN: ${{ github.token }}
PROBLEMS: ${{ steps.check.outputs.problems }}
run: |
title="Link check: ${PROBLEMS} sources need a look"
existing=$(gh issue list --state open --label correction --json number,title \
--jq '[.[] | select(.title | startswith("Link check:"))][0].number // empty')
if [ -n "$existing" ]; then
gh issue edit "$existing" --title "$title" --body-file link-report.md
else
gh issue create --title "$title" --label correction --body-file link-report.md
fi
28 changes: 10 additions & 18 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,6 @@ name: Deploy GitHub Pages
on:
push:
branches: [main]
paths:
- "catalog/**"
- "docs/**"
- "site/**"
- ".github/workflows/pages.yml"
workflow_dispatch:
# Weekly rebuild so the freshness ages never lag the calendar by more than
# about a week when nothing is pushed.
Expand All @@ -24,30 +19,27 @@ concurrency:
cancel-in-progress: true

jobs:
# The same tests, validation and page checks as a pull request runs.
validate:
uses: ./.github/workflows/ci.yml

build:
needs: validate
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v7

- name: Setup Node
uses: actions/setup-node@v4
uses: actions/setup-node@v7
with:
node-version: "20"

- name: Test catalog checks
run: node site/checks.test.mjs
- name: Test site build modules
run: node site/lib/lib.test.mjs

- name: Validate catalog
run: node site/validate.mjs
node-version-file: .nvmrc

- name: Build site
run: node site/build.mjs

- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v3
uses: actions/upload-pages-artifact@v5
with:
path: site/dist

Expand All @@ -60,4 +52,4 @@ jobs:
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
uses: actions/deploy-pages@v5
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ Desktop.ini
.vscode/
.idea/

# Dependencies (the repo has none, but npx and editors may add them)
node_modules/

# Logs / caches
*.log
__pycache__/
Expand Down
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
24
34 changes: 16 additions & 18 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,16 @@ Please follow the [Code of Conduct](CODE_OF_CONDUCT.md).

## Adding an entry

One new markdown file is enough — the website rebuilds from frontmatter on deploy.

1. Copy [`catalog/TEMPLATE.md`](catalog/TEMPLATE.md).
2. Save it as `catalog/<category>/<id>.md` using a short kebab-case `id`.
3. Fill every frontmatter field. Prefer primary URLs over mirror/aggregator pages.
4. `id` must be unique across the **whole** catalog. A mixed kit already listed in another category is a duplicate, not a second entry.
5. Verify the license on the live source page the day you submit.
6. For `status: active`, include an `## Evidence` line with a short quote from that page.
7. Bump `expectedEntryCount` in [`site/config.json`](site/config.json) by the number of files you added (or lowered if you removed some).
8. Run `node site/validate.mjs` — it must exit 0.
9. Run `node site/build.mjs`. It renders your entry's page and fails if the body uses markdown the site does not support (tables, code fences, blockquotes, images, raw HTML, `###` headings, numbered lists) or links to a file that does not exist.
10. Optional 2D/UI fields: `grid_dimensions`, `camera_perspective`, `hardware_tags`, `attribution_string` (see [`TEMPLATE.md`](catalog/TEMPLATE.md)).
11. Add a row to the matching category `README.md`. This is required: the validator fails an entry that is not listed there, and the row's licence cell must match your frontmatter.
12. Optional: add the `id` to `site/config.json` → `featured` to pin it under Safe starting points.
The website rebuilds from frontmatter on deploy. Node 22 or newer is all you need; there
are no dependencies to install.

1. Run `node site/new-entry.mjs <category> <id>` (or `npm run new-entry -- <category> <id>`). It copies [`catalog/TEMPLATE.md`](catalog/TEMPLATE.md) to `catalog/<category>/<id>.md` with the id, category and today's date filled in, and refuses an id already used anywhere in the catalog. Use a short kebab-case `id`; a mixed kit already listed in another category is a duplicate, not a second entry.
2. Verify the license on the live source page the day you submit, and fill every frontmatter field from it (rules below). Prefer primary URLs over mirror/aggregator pages.
3. Write the body: a one-paragraph summary, `## Notes`, and `## Evidence` with a dated line quoting the source, such as `- Live page (2026-09-25): "Free for commercial use"`. Every Evidence section needs at least one date, and `verified` may not be newer than the newest one. The scaffold starts at `status: needs-review`; set `active` once the licence, the commercial stance and the credit requirement are all settled.
4. Add a row to the matching category `README.md`. This is required: the validator fails an entry that is not listed there, and the row's licence cell must match your frontmatter.
5. Run `node site/sync-counts.mjs` (`npm run counts`). It updates every place the repo restates the entry count: both category count tables, the README badge and "Browse N sources" line, and `expectedEntryCount` in [`site/config.json`](site/config.json).
6. Run `npm run check`: the check tests, `node site/validate.mjs` and `node site/build.mjs`, all of which must pass. The build renders your entry's page and fails if the body uses markdown the site does not support (tables, code fences, blockquotes, images, raw HTML, `###` headings, numbered lists) or links to a file that does not exist.
7. Optional: add the `id` to `site/config.json` → `featured` to pin it under Safe starting points.

To add a starter stack (one pick per need for a kind of game), follow [`stacks/README.md`](stacks/README.md).

Expand All @@ -44,13 +40,15 @@ To add a starter stack (one pick per need for a kind of game), follow [`stacks/R
- `license_spdx`: the SPDX identifier the vocabulary maps your `license` to. The validator rejects a missing one when a mapping exists, and an invented one when it does not.
- `commercial`: `true` / `false` / `unknown` / `varies`. Use `varies` for aggregators where some files are commercial-ok and others are not. The site still shows these under Commercial OK, labeled **per-file review**, so they are not silently excluded and not silently treated as a blanket grant.
- `attribution_required`: `true` / `false` / `unknown`. If the licence normally requires credit (CC-BY) but the publisher waives it, set `false` and add a `- Attribution waived:` line in Notes saying so.
- `attribution_string`: optional copy-paste credit.
- `attribution_string`: the copy-paste credit line. Required when `attribution_required` is `true`; optional otherwise.
- `publisher`: optional. Name the **rights-holding publisher**, never the host. Set it whenever that publisher has more than one catalog entry, so the entries group; setting it on a publisher that currently has only one entry is also fine and saves a backfill later. Do not set it to a generic host or distributor (GitHub, Hugging Face, itch.io, OpenGameArt, the Internet Archive, Google Fonts) or to a distributor that does not hold the rights. Distinguish sibling organisations that are genuinely different rights holders: `blender.md` (the application, Blender Foundation) carries no `publisher`, while the asset bundles under `studio.blender.org` carry `Blender Studio`. Kenney, Quaternius, KayKit, LuizMelo, 0x72, Blender Studio, Material Maker, Alif Type, GGBotNet and 3dmodelscc0 are the largest groups in use today; treat that as illustrative, not as the permitted set.
- `subcategories`: lower-case kebab-case (`base-meshes`, `field-recordings`). Reuse a value already in the catalog before inventing one, and do not add a variant of an existing value that differs only in plural or spelling. Where both forms were in use, the more common one was kept (`characters`, `environment`, `interior`, `tileset`, `vectors`).
- `subcategories`: lower-case kebab-case (`base-meshes`, `field-recordings`). Reuse a value already in the catalog before inventing one, and do not add a variant of an existing value that differs only in plural or spelling. Where both forms were in use, the more common one was kept (`characters`, `environment`, `interior`, `tileset`, `vectors`, `pixel`, `impulse-responses`, `base-meshes`). Retired spellings are listed in [`site/value-aliases.json`](site/value-aliases.json), which the validator enforces (V19); add a line there when you merge two values.
- `formats`: what you actually get, in one of four kinds. **File formats**, as commonly written: usually upper case (`PNG`, `FBX`, `JSON`, `VOX`), tool-specific ones as the tool writes them (`gdshader`, `tmx`, `ktx2`). **Engine or language targets**, as the product writes them (`glTF`, `Godot`, `Unity`, `Python`, `React`). **Delivery types** for software with no file format of its own, lower-case kebab-case (`godot-addon`, `blender-extension`, `npm`, `cli`, `library`, `desktop-app`, `mobile-app`, `middleware`, `model`). And `various` for an aggregator whose files come in too many formats to list. Platforms (`windows`, `macos`, `ios`) and descriptions (`heightfield`, `examples`) are `tags`, not formats. Reuse the spelling already in the catalog.
- Frontmatter is one `key: value` per line; lists are `[a, b]` or indented `- item` lines. A key given twice is an error. Entry files may not contain emoji.
- `grid_dimensions` / `camera_perspective` / `hardware_tags`: optional metadata for 2D and UI entries. `3d` and `characters` entries leave `camera_perspective` out: it describes a 2D camera, and those entries have none.
- `verified`: ISO date (`YYYY-MM-DD`) of your last license check.
- `status`: `active` | `needs-review` | `deprecated`. `active` means the licence, the commercial stance and the credit requirement are all settled: an `unknown` in `license`, `commercial` or `attribution_required` keeps an entry at `needs-review`. The validator enforces this (V14), and rejects `formats`, `subcategories` or `tags` values that differ only by case, punctuation or a trailing "s" from one already in use (V13).
- `url`: an `https://` address (`http://` only where the source has no https). Other schemes are rejected.
- `verified`: ISO date (`YYYY-MM-DD`) of your last license check. It must be a real date, no later than tomorrow in UTC, and no newer than the newest date in `## Evidence` (V8): a new `verified` date needs a new dated Evidence line from the same check. Dates inside URLs do not count as Evidence dates.
- `status`: `active` | `needs-review` | `deprecated`. A `deprecated` entry needs a `- Deprecated:` line in its body giving the reason (V9). `active` means the licence, the commercial stance and the credit requirement are all settled: an `unknown` in `license`, `commercial` or `attribution_required` keeps an entry at `needs-review`. The validator enforces this (V14), and rejects `formats`, `subcategories` or `tags` values that differ only by case, punctuation or a trailing "s" from one already in use (V13).

## License verification checklist

Expand Down
16 changes: 12 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -268,11 +268,14 @@ and follow the [Code of Conduct](CODE_OF_CONDUCT.md).
<details>
<summary>Run the checks and preview the site locally</summary>

Node 22 or newer; there is nothing to install.

```bash
node site/checks.test.mjs # the checks, against known-bad fixtures
node site/validate.mjs # the catalog, against the checks
node site/build.mjs # builds the site into site/dist
npx --yes serve site/dist # preview at http://localhost:3000
npm run check # all of the below, in the order CI runs them
npm test # the checks against known-bad fixtures, and the site modules' tests
npm run validate # the catalog, against the checks (node site/validate.mjs)
npm run build # builds the site into site/dist (node site/build.mjs)
npm run serve # preview at http://localhost:3000
```

</details>
Expand All @@ -297,14 +300,19 @@ npx --yes serve site/dist # preview at http://localhost:3000
│ ├── godot-budget-stack.md research-index.md
│ ├── review-ledger.md every review pass and what it changed
│ └── images/readme/ publisher stills used in this README
├── stacks/ starter stacks: one pick per need for a kind of game
├── site/
│ ├── validate.mjs catalog integrity checks
│ ├── checks.mjs the checks, as pure functions
│ ├── checks.test.mjs fixtures proving each check fails on bad input
│ ├── new-entry.mjs starts an entry from the template
│ ├── sync-counts.mjs rewrites every restated entry count
│ ├── license-vocabulary.json the licence values the catalog accepts
│ ├── spdx-allowed.json SPDX identifiers in use
│ ├── build.mjs config.json static site generator and its settings
│ ├── lib/ page renderers and shared helpers, with lib.test.mjs
│ └── public/ page, styles and script
├── package.json npm scripts only; no dependencies
└── RESEARCH/README.md research archive index (drafts are gitignored)
```

Expand Down
2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ and a quote from the live source page.

Report XSS or other issues in `site/` through
[GitHub private vulnerability reporting](https://github.com/TMHSDigital/Free-Game-Dev-Assets/security/advisories/new)
if it is enabled, otherwise open an issue without a proof-of-concept payload.
(enabled for this repository). If you cannot use it, open an issue without a proof-of-concept payload.

Do not open issues for third-party asset files. This repo does not host them.
1 change: 1 addition & 0 deletions catalog/2d/ansimuz-industrial-parallax.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
id: ansimuz-industrial-parallax
name: Industrial Parallax Background (ansimuz)
url: https://opengameart.org/content/industrial-parallax-background
publisher: ansimuz
category: 2d
subcategories: [backgrounds, pixel]
license: CC0
Expand Down
3 changes: 2 additions & 1 deletion catalog/2d/ansimuz-sunnyland.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
id: ansimuz-sunnyland
name: SunnyLand (ansimuz)
url: https://ansimuz.itch.io/sunny-land-pixel-game-art
publisher: ansimuz
category: 2d
subcategories: [sprites, tiles, pixel, platformer, audio]
subcategories: [sprites, tileset, pixel, platformer, audio]
license: CC0
license_spdx: CC0-1.0
commercial: true
Expand Down
1 change: 1 addition & 0 deletions catalog/2d/bondoki-rotating-gems.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ commercial: true
attribution_required: false
formats: [GIF, PNG]
tags: [cc0, match-3, gems, jewels, animated, puzzle, casual, mobile]
grid_dimensions: 52x52
verified: 2026-09-23
status: active
---
Expand Down
2 changes: 1 addition & 1 deletion catalog/2d/dcss-tiles.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ id: dcss-tiles
name: Dungeon Crawl Stone Soup Tiles
url: https://github.com/crawl/tiles
category: 2d
subcategories: [tiles, sprites, monsters, items]
subcategories: [tileset, sprites, monsters, items]
license: varies
commercial: unknown
attribution_required: unknown
Expand Down
2 changes: 1 addition & 1 deletion catalog/2d/gameart2d-freebies.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ id: gameart2d-freebies
name: GameArt2D freebies
url: https://www.gameart2d.com/freebies.html
category: 2d
subcategories: [sprites, tiles, ui, platformer]
subcategories: [sprites, tileset, ui, platformer]
license: CC0
license_spdx: CC0-1.0
commercial: true
Expand Down
2 changes: 1 addition & 1 deletion catalog/2d/kenney-pixel-platformer.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ name: Kenney Pixel Platformer
url: https://kenney.nl/assets/pixel-platformer
publisher: Kenney
category: 2d
subcategories: [sprites, tiles, platformer]
subcategories: [sprites, tileset, platformer]
license: CC0
license_spdx: CC0-1.0
commercial: true
Expand Down
2 changes: 1 addition & 1 deletion catalog/2d/kenney-ui-pack.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ name: Kenney UI Pack
url: https://kenney.nl/assets/ui-pack
publisher: Kenney
category: 2d
subcategories: [ui, gui]
subcategories: [ui]
license: CC0
license_spdx: CC0-1.0
commercial: true
Expand Down
4 changes: 2 additions & 2 deletions catalog/2d/lospec.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ id: lospec
name: Lospec
url: https://lospec.com
category: 2d
subcategories: [palettes, pixel-art]
subcategories: [palettes, pixel]
license: varies
commercial: unknown
attribution_required: unknown
formats: [GPL, PAL, ASE, PNG]
formats: [gpl-palette, PAL, ASE, PNG]
tags: [palettes, pixel-art, retro]
verified: 2026-07-20
status: needs-review
Expand Down
Loading
Loading