Static assets served by sendou.ink: badges, images, sounds,
icons, and Splatoon stage planner maps. Everything in assets/ is
mirrored to a DigitalOcean Space on every push to main (see Deployment).
assets/
badges/ badge animations (.gif)
img/ general images, mostly .avif (+ some .png/.gif)
planner-maps/ stage maps for the build planner (.png)
scanner/ glyph & planner-signature atlases for the sendou.ink scanner
sounds/ UI / notification sounds (.wav)
svg/ icons (.svg)
planner-maps.js script that renames raw map exports into the naming scheme
planner-maps-yaga.js variant of the above for a different input layout
homemade.json metadata for community-made badges (see below)
check-homemade-badges.js validates homemade.json (runs in CI)
Anything outside assets/ is tooling/source and is not deployed.
Raw map exports are dropped into an input/ folder (gitignored) and normalized
into <stageId>-<mode>-<type>.png by the generator scripts:
node planner-maps.js # reads input/, writes assets/planner-maps/
node planner-maps-yaga.js # reads input/overhead/, writes assets/planner-maps/- stageId — numeric id derived from the stage abbreviation (
ScG→0, …). - mode — game mode (
SZ,RM,CB, …). - type — map variant:
OVER(overhead),MINI(minimap),ITEMS.
homemade.json maps each community-made badge to its display
name and the Discord id of its author. sendou.ink reads it from this repo's main
branch to sync badge metadata into its database. Each entry's key is the badge
file name, which must have matching assets/badges/<key>.avif and
assets/badges/<key>.gif files.
{
"my-badge": {
"displayName": "My Badge",
"authorDiscordId": "123456789012345678"
}
}Keys are kept in alphabetical order and display names must be unique.
check-homemade-badges.js enforces all of this
(shape, ordering, uniqueness, encoding, and that the image files exist). It runs
on every pull request that touches homemade.json or assets/badges/ (see
the workflow) and can be run
locally:
node check-homemade-badges.js.github/workflows/deploy-assets.yml
syncs assets/ to DigitalOcean Spaces using s3cmd.
- Trigger: push to
mainthat touchesassets/**(or the workflow file), or a manual run via the Actions tab ("Run workflow"). - Sync:
s3cmd sync --delete-removedmakes the Space an exact mirror ofassets/— files deleted here are deleted in the Space. Uploads are content-addressed (size + md5), so unchanged files are skipped. - Content types:
.aviffiles are uploaded in a second pass with an explicitimage/aviftype, since s3cmd can't infer it from the extension. - Visibility: objects are uploaded with a public-read ACL.
Set these under Settings → Secrets and variables → Actions.
| Type | Name | Example |
|---|---|---|
| Secret | SPACES_ACCESS_KEY |
DigitalOcean Spaces access key |
| Secret | SPACES_SECRET_KEY |
DigitalOcean Spaces secret key |
| Variable | SPACES_ENDPOINT |
nyc3.digitaloceanspaces.com |
| Variable | SPACES_BUCKET |
Space (bucket) name |
| Variable | SPACES_PREFIX |
optional path prefix, or empty |
The first deploy uploads the full asset set; later deploys only transfer what changed.