Skip to content
Merged
  •  
  •  
  •  
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

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

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/links.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

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

Expand Down
11 changes: 7 additions & 4 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,21 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# The sitemap's lastmod dates come from git history.
fetch-depth: 0

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

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

- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v5
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
with:
path: site/dist

Expand All @@ -52,4 +55,4 @@ jobs:
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v5
uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1
52 changes: 52 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: Dataset release

# A versioned snapshot of the catalog a tool can pin: tag vYYYY.MM.DD and
# attach data.json, catalog.csv, the two feeds and llms.txt. Monthly, and on
# demand from the Actions tab. The release notes are the newest section of
# docs/review-ledger.md. A tag that already exists is left alone.
on:
schedule:
- cron: "0 9 1 * *"
workflow_dispatch:

permissions:
contents: write

jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# The sitemap's lastmod dates come from git history.
fetch-depth: 0

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

- name: Check and build
run: npm test && node site/validate.mjs && node site/build.mjs

- name: Create the release
env:
GH_TOKEN: ${{ github.token }}
run: |
tag="v$(date -u +%Y.%m.%d)"
if gh release view "$tag" >/dev/null 2>&1; then
echo "release $tag already exists; nothing to do"
exit 0
fi
# Newest ledger section: from the first "## " heading to the next.
awk '/^## /{n++} n==1' docs/review-ledger.md > release-notes.md
{
echo
echo "Attached: \`data.json\` (full catalog), \`catalog.csv\`, \`feed.xml\` (new sources), \`reverified.xml\` (recent licence re-reads), \`llms.txt\`."
echo "Field reference: https://github.com/${GITHUB_REPOSITORY}/blob/main/site/README.md"
} >> release-notes.md
gh release create "$tag" \
--title "Catalog $tag" \
--notes-file release-notes.md \
site/dist/data.json site/dist/catalog.csv site/dist/feed.xml site/dist/reverified.xml site/dist/llms.txt
46 changes: 46 additions & 0 deletions .github/workflows/reverify.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Monthly re-verify batch

# On the first of each month: pick the live entries whose licence was read
# longest ago (more than 150 days), about a tenth of the catalog, and open or
# update one checklist issue asking for them to be re-read at the source.
# It never edits an entry.
on:
schedule:
- cron: "0 8 1 * *"
workflow_dispatch:

permissions:
contents: read
issues: write

jobs:
reverify:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

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

- name: Pick the batch
id: pick
run: node site/reverify-batch.mjs --report reverify-batch.md

- name: Open or update the batch issue
if: steps.pick.outputs.count != '0'
env:
GH_TOKEN: ${{ github.token }}
COUNT: ${{ steps.pick.outputs.count }}
run: |
month=$(date -u +%Y-%m)
title="Re-verify batch ${month}: ${COUNT} entries due a licence re-read"
existing=$(gh issue list --state open --label "help wanted" --json number,title \
--jq '[.[] | select(.title | startswith("Re-verify batch "))][0].number // empty')
if [ -n "$existing" ]; then
gh issue edit "$existing" --title "$title" --body-file reverify-batch.md
else
gh issue create --title "$title" --label "help wanted" --label "good first issue" --body-file reverify-batch.md
fi
22 changes: 22 additions & 0 deletions CITATION.cff
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
cff-version: 1.2.0
message: "If you use this catalog in research or teaching, please cite it as a dataset."
type: dataset
title: "Free Game Dev Assets: a dated, sourced catalog of free game assets and tools with licence metadata"
authors:
- name: "TMHSDigital"
website: "https://github.com/TMHSDigital"
repository-code: "https://github.com/TMHSDigital/Free-Game-Dev-Assets"
url: "https://tmhsdigital.github.io/Free-Game-Dev-Assets/"
license: CC0-1.0
date-released: "2026-10-22"
keywords:
- game-development
- game-assets
- licensing
- open-data
- catalog
abstract: >-
A curated catalog of free, commercially usable game assets and tools. Every
entry records the licence as read at the source on a stated date, the
commercial stance, the credit requirement and dated evidence quotes. The
catalog metadata is CC0; the linked assets keep their own licences.
14 changes: 11 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ Please follow the [Code of Conduct](CODE_OF_CONDUCT.md).

## Adding an entry

The website rebuilds from frontmatter on deploy. Node 22 or newer is all you need; there
are no dependencies to install.
The website rebuilds from frontmatter on deploy. Node 22 or newer is all you need (CI and
`.nvmrc` use 24); 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.
Expand All @@ -49,7 +49,7 @@ To add a starter stack (one pick per need for a kind of game), follow [`stacks/R
- `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`, `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. The allowed values are the closed list in [`site/format-vocabulary.json`](site/format-vocabulary.json), which the validator enforces (V20): reuse a listed spelling, and add a genuinely new format there in the same change that first uses it. Its `groups` drive the site's Format filter, so a new format that belongs to a group (another glTF or font container) goes into that group too.
- `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. The allowed values are the closed list in [`site/format-vocabulary.json`](site/format-vocabulary.json), which the validator enforces (V20): reuse a listed spelling, and add a genuinely new format there in the same change that first uses it. Its `groups` drive the site's Format filter, so a new format that belongs to a group (another glTF or font container) goes into that group too. A format outside every group is still searchable and shown on the card, but the Format filter cannot select it.
- `tags`: lower-case kebab-case words describing the content (`low-poly`, `modular`, `platformer`). Do not restate another field: the licence (`cc0`, `mit`, `ofl`, `open-source`, `royalty-free`), the credit requirement (`no-attribution`) or the entry's own `publisher` (`kenney`) are rejected (V19 via `site/value-aliases.json`, V21), because search and the filters read those fields directly and a copied tag drifts. A `varies` entry lists its licence mix in the body, not in tags.
- 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.
Expand All @@ -58,6 +58,7 @@ To add a starter stack (one pick per need for a kind of game), follow [`stacks/R
- `ue_versions`: optional, the Unreal Engine versions the source itself states, as `5.4`, `5.4-5.8` or `5.4+` (V23). Say in Notes where the range came from (README, `.uplugin`, release names, listing). Never infer it.
- Unreal plugins use `subcategories: [unreal, ...]` and `formats: [unreal-plugin]`; Unreal projects and content packs use `formats: [Unreal]`. The validator rejects a GPL or LGPL licence on either, because the Unreal Engine EULA forbids combining the engine with that code (see [`docs/unreal.md`](docs/unreal.md)).
- `url`: an `https://` address (`http://` only where the source has no https). Other schemes are rejected.
- `added`: ISO date (`YYYY-MM-DD`) the entry joined the catalog. `npm run new-entry` fills it in; it never changes afterwards (V26: a real date, not in the future). The "new sources" feed and the home page's "New in the last 30 days" line key on it, so a re-verification sweep does not look like a wave of new entries.
- `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).

Expand All @@ -73,6 +74,13 @@ To add a starter stack (one pick per need for a kind of game), follow [`stacks/R
- [ ] Entry does not claim rights we don’t have
- [ ] Tool entries distinguish **software license** from **exported asset** ownership

Verification ages. On the first of each month a workflow opens a "Re-verify batch" issue
listing the live entries whose licence was read longest ago (over 150 days, about a tenth
of the catalog). Each item is a self-contained re-read: open the source, confirm the
terms, bump `verified` and add a dated Evidence quote, or set `needs-review` and say what
changed. Those issues carry `good first issue`; pick one if you want a first contribution
with no code.

## Submitting your own source

Publishers are welcome to list their own work. Say so in the pull request, write the entry as a third party would (facts the source states, no comparisons with other entries, no ranking language), and start it at `needs-review` so a maintainer re-reads the licence before it goes `active`. A free tier of a paid product is listed as the free tier only, with the paid tier named in Notes and not linked as the entry's `url`. A one-time notice or upsell inside a tool is recorded in Notes as a fact.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ Please 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.
Node 22 or newer (CI and `.nvmrc` use 24); there is nothing to install.

```bash
npm run check # all of the below, in the order CI runs them
Expand Down
68 changes: 34 additions & 34 deletions ROADMAP.md
Original file line number Diff line number Diff line change
@@ -1,34 +1,34 @@
# Roadmap

Where help is most useful. The rule behind all of it: a wrong licence claim is worse than a
missing entry, so freshness and accuracy come before volume.

## Open to contributors

- **Thin categories:** video, animation and environment are still the smallest (counts in the
[category table](catalog/README.md)). See [CONTRIBUTING.md](CONTRIBUTING.md) and the "new
source" issue form.
- **Optional fields:** `camera_perspective` and `grid_dimensions` on 2D entries
([#41](https://github.com/TMHSDigital/Free-Game-Dev-Assets/issues/41)). Small, well-scoped, a
good first PR.
- **Starter stacks:** audio, a game-jam kit, Unity, Unreal and VR are not covered yet. Use the
"request an asset" form to add to a stack's Gaps list.
- **Corrections:** a licence that changed or a link that moved. The weekly link check opens an
issue listing them.

## Planned

- Re-verification in smaller, regular batches, so the July 2026 verifications do not all expire
together in mid-2027.
- A changelog page generated from [docs/review-ledger.md](docs/review-ledger.md).
- Full-text search over entry bodies (Notes and Evidence), not only names, tags and summaries.

## Done

- Site search across names, tags and summaries, with licence, format and category filters.
- Accessibility fixes: focus handling, contrast, heading outline and tap targets (Lighthouse
accessibility 100).
- Thin categories topped up: 15 video, animation and environment sources (2026-10-04).

- Machine-readable exports: `data.json`, `catalog.csv` and an Atom feed (`feed.xml`) of recently
verified entries.
# Roadmap
Where help is most useful. The rule behind all of it: a wrong licence claim is worse than a
missing entry, so freshness and accuracy come before volume.
## Open to contributors
- **Thin categories:** video, animation and environment are still the smallest (counts in the
[category table](catalog/README.md)). See [CONTRIBUTING.md](CONTRIBUTING.md) and the "new
source" issue form.
- **Optional fields:** `camera_perspective` and `grid_dimensions` on 2D entries
([#41](https://github.com/TMHSDigital/Free-Game-Dev-Assets/issues/41)). Small, well-scoped, a
good first PR.
- **Starter stacks:** audio, a game-jam kit, Unity, Unreal and VR are not covered yet. Use the
"request an asset" form to add to a stack's Gaps list.
- **Corrections:** a licence that changed or a link that moved. The weekly link check opens an
issue listing them.
## Planned
- Re-verification in smaller, regular batches, so the July 2026 verifications do not all expire. Since 2026-10 the `reverify.yml` workflow opens a monthly checklist issue of the entries checked longest ago (over 150 days, about a tenth of the catalog each month)
together in mid-2027.
- A changelog page generated from [docs/review-ledger.md](docs/review-ledger.md).
- Full-text search over entry bodies (Notes and Evidence), not only names, tags and summaries.
## Done
- Site search across names, tags and summaries, with licence, format and category filters.
- Accessibility fixes: focus handling, contrast, heading outline and tap targets (Lighthouse
accessibility 100).
- Thin categories topped up: 15 video, animation and environment sources (2026-10-04).
- Machine-readable exports: `data.json`, `catalog.csv` and an Atom feed (`feed.xml`) of recently
verified entries.
Loading
Loading