Makers guide

Games, films, pets and small tools, published as HTML.

Overview

Format
Offline HTML package (ZIP)
Entry
index.html at the root
Size
ZIP ≤ 150 MB
Cover
16:10, ≤ 1 MB
Hooks
spareStart · sparePause · spareSnapshot
Review
By a person
Rights
Stay with the maker
Example: Hextris, GPL-3.0 open-source port

Package

ItemRequirement
Entryindex.html and manifest.json at the ZIP root
PathsRelative only; no absolute paths, .. or remote URLs
SizeZIP ≤ 150 MB
NetworkOffline, no external scripts, no tracking
SoundMuted by default, stops on pause
WidthWorks at 320 px, keyboard and touch
ProgressJSON-serialisable snapshot, ideally < 16 KiB
Languagezh-Hans · en · fr, with a fallback
Leave outnode_modules, .git, keys, system files

manifest.json

{
  "schemaVersion": 1,
  "id": "com.example.tiny-garden",
  "name": "片刻花园",
  "version": "1.0.0",
  "category": "plant",
  "entry": "index.html",
  "permissions": [],
  "storage": "local",
  "lifecycle": {
    "start": "spareStart",
    "pause": "sparePause",
    "snapshot": "spareSnapshot"
  }
}
FieldMeaning
idReverse domain or hyphenated unique id
nameWork name, same as the submission form
versione.g. 1.0.0; bump on resubmission
categorygame · pet · story · novel · plant · utility · other
entryRelative path to the HTML file
permissionsEmpty [] for now
storagelocal or none
lifecycleNames of the three hooks

Lifecycle hooks

FunctionCalled whenReturns
spareStart(saved, language)AI starts generating; saved is the last snapshot or null—
sparePause()AI is done or the window hides; stop timers, animation and soundSnapshot
spareSnapshot()Progress needs savingSmall JSON object
// Called when AI starts generating, with the last saved progress
window.spareStart = (saved, language) => {
  state = saved ?? newGame();
  render(language);
};
// Called when AI is done or the window hides: stop animation and sound now
window.sparePause = () => {
  stopTimersAndAudio();
  return window.spareSnapshot();
};
// Return a small JSON-serialisable object to resume next time
window.spareSnapshot = () => ({ ...state });
FilePurposeSize
index.htmlEntry, edit this4.0 KB
manifest.jsonName, version, category307 B
preview.htmlSimulates Spare locally3.3 KB
README.mdFull guide6.8 KB
LICENSEMIT1.1 KB

Local preview

cd spare-plugin-starter
python3 -m http.server 8000
# http://localhost:8000/preview.html

Submit and review

  1. MakeEdit index.html; test start, pause and resume in preview.html
  2. SubmitUpload the ZIP, a 16:10 cover and a description; list asset sources and licences
  3. ReviewChecked by a person: offline, no tracking, pauses, works at 320 px, assets licensed
  4. LiveListed on the Works page under your name

Review status

StatusMeaningNext
PendingWaiting for review—
PublishedListed on the Works pageAsk for removal anytime
ReturnedNot accepted, reason givenFix, bump the version, resubmit
UnpublishedNo longer listedContact support@spare.cool

Licence

CaseRule
You own itSpare gets a licence to host and distribute it. Ask for removal anytime.
Licensed assetsYou need rights to the code, images and music. List licences or sources in the README.
Open-source portsKeep the original licence and credits and describe your changes. Hextris, for example, is GPL-3.0.
Template codeMIT. Modify and publish freely.

FAQ

01

Do I need to code?

No. AI can write it.

02

Who owns my work?

You do. Spare gets a licence to host and distribute it; you can ask for removal anytime.

03

Can it use the network?

No. No tracking or third-party scripts either.

Let people pick up where they left off

Web works can use this checkpoint protocol: 12 recent works, up to 32 KB each. Offline checkpoints stay on the device; players choose which save to keep on a conflict. Version and validate every restored field.

// Only your work's checkpoint crosses the sandbox boundary.
let state = { version: 1, chapter: 1 };
window.addEventListener('message', event => {
  if (event.source !== parent || event.data?.type !== 'spare:restore') return;
  const saved = event.data.snapshot;
  if (saved?.version === 1 && Number.isInteger(saved.chapter)) {
    state.chapter = Math.max(1, Math.min(100, saved.chapter));
    render();
  }
});
parent.postMessage({ type: 'spare:ready' }, '*');
function checkpoint() {
  parent.postMessage({ type: 'spare:checkpoint', snapshot: state }, '*');
}
// Call checkpoint() after a meaningful action, not on every animation frame.

Offline desktop works keep spareStart(saved), sparePause() and spareSnapshot(). Spare manages subscriptions and friend permissions; works never receive account tokens. Updates still go through review before appearing for subscribers.