# Use images and sound

## Media rules

- Accepted files: images and audio (PNG, JPG, JPEG, WEBP, GIF, MP3, up to 5 MB each) and 3D models as binary glTF (.glb, up to 15 MB each).
- Each product has one media library, shared across its versions. A studio scratchpad's media moves to the product when the product is created.
- Every file gets a tag: lowercase letters, numbers, and hyphens, taken from its filename (win-jingle for win-jingle.mp3). A tag can be renamed, and swapping the file behind a tag needs no template change.
- Every render receives the library as the files map: tag to hosted URL.
- Templates are first party only, with no allowlist: a template containing an external URL (hardcoded http(s) or protocol relative //domain), in its source or in its rendered output, is rejected at submission, and the preview's security policy blocks external images, fonts, and audio from loading. The files map is the only supported way to reference media.

## The media workflow

1. Add the file to the product's media library. Connected, call `request_media_upload` for each file (Connect your AI explains how the upload reaches the marketplace in each client). In the browser, the creator drops files into the studio's Media panel.
2. Reference each file only through the files map, by its tag: for example {{ files['background'] }} as an image src or a CSS background via an inline style, {{ files['win-jingle'] }} as an audio src, and {{ files['prize-box'] }} as the URL a 3D loader fetches. The Copy reference button beside each file in the studio's Media panel copies its reference.
3. Play audio only after a user interaction, because browsers block autoplay.

- When an image or sound changes, replace the file behind its existing tag, because the template then needs no change.
- When an asset is on a website, ask the creator for the file instead of linking to it, because an external URL fails submission.

## 3D models

Upload a 3D model as one binary glTF file (.glb), up to 15 MB. A game shows it with an approved library such as three.js (see below). Exporting is a step for the creator in Blender:

1. Apply transforms: select the model's objects, then Object > Apply > All Transforms, so it loads at the scale, rotation, and position seen in Blender.
2. Choose File > Export > glTF 2.0 (.glb/.gltf).
3. Set Format to glTF Binary (.glb). This embeds every texture in the one file, and embedded textures load automatically.
4. Leave compression off: keep Compression (Draco) unticked, and do not run the file through a tool that adds meshopt or KTX2 compression afterwards.
5. Upload the .glb and reference it by its tag.

When an upload is refused, quote the message to the creator, because each one names the export fix:

- A .blend file is Blender's own project format, which browsers cannot load. The upload says: "Browsers cannot load .blend files. Export from Blender as .glb (File > Export > glTF 2.0, format glTF Binary)."
- A .gltf file points at separate .bin and texture files, and one files map entry cannot carry several files. The upload says: "Upload a single .glb instead: .gltf files reference separate files the files map cannot express."
- A compressed model (Draco, KTX2, or meshopt) needs decoders the platform does not ship. The upload says: "Compressed models need decoders the platform does not ship yet. Export without Draco, KTX2, or meshopt compression."
- A file that is not real binary glTF 2.0 is refused with "prize-box.glb is not a valid .glb file. Export it from Blender as glTF 2.0, format glTF Binary." (naming the file).

## Approved libraries

A template's own JavaScript always stays inline. The one exception is an approved library: a third party build staff have vetted (initially three.js, bundling its core, GLTFLoader, and OrbitControls), identified by the SHA-256 of its exact bytes and hosted once for every product. The current builds, with versions, contents, hashes, and licence notices, are listed at https://marketplace.rafflex.io/docs/libraries.md

1. Add the build to the media library. Connected, call `attach_approved_library`. In the browser, the creator presses Add approved library in the studio's Media panel and picks a build. It joins the media library under a tag (three for the three.js bundle) and does not count toward the product's storage.
2. Reference it only through the files map: `import * as THREE from "{{ files['three'] }}";` inside a `<script type="module">`, or `<script src="{{ files['tag'] }}"></script>` for a classic script. A script src or module import that is anything else is rejected.
3. Uploading a .js file works only when its bytes are identical to an approved build. Any other script is refused with "This file is not an approved library. Put your own code in the template, or email support@rafflex.io to request a library."
4. When the idea needs a library that is not listed, tell the creator they can email support@rafflex.io with the library, the exact version, and what they want to build with it, because staff vet every build before it is approved.

## Checks before you finish

- Every file and library the template uses is in the media library and referenced only through the files map.
- Audio starts only after a user interaction, and the template check passes.

## Getting the data into JavaScript

Game and interaction logic lives in JavaScript over the injected data; Twig is for markup and simple conditionals only. Inject the context with the json filter:

<script>
    const plays = {{ plays|json }};
    const files = {{ files|json }};
    const settings = {{ settings|json }};
</script>

Alpine.js is already available on the page if you prefer it over vanilla JavaScript. The only other library a template may load is an approved library from the product's media library, and only through its files reference (for example `import * as THREE from "{{ files['three'] }}";` inside a `<script type="module">`). Never load any other library or script.

## Submission rules (hard constraints)

- Templates are first party only, with no allowlist and no exceptions: no external domain may appear anywhere, in any attribute, style, or script. That means no CDN scripts, no Google Fonts, no hotlinked images, no absolute http(s) URLs, and no protocol-relative //domain URLs. Reference only relative platform paths and uploaded media through the files map by tag ({{ files['background'] }}). Every layer enforces this: the static checks, a scan of the rendered output, the preview's security policy, human review, and periodic fleet re-scans.
- No <form> elements, formaction attributes, or <link> tags. Game entry flows through the platform, inputs bound to Alpine state cover every interactive need, and styles ship inline.
- No network or exfiltration APIs, even aliased: fetch, XMLHttpRequest, WebSocket, EventSource, sendBeacon, WebRTC, and the entire navigator object are banned as bare identifiers, along with eval, Function, constructor or __proto__ or Reflect access, import.meta, CSS @import, data:text/html, and hex or unicode escape sequences.
- Everything stays inline in the single template: styles in a <style> block, JavaScript in a <script> block. The one exception is an approved library: a script src, or an import in a <script type="module">, may load one, and only through its files reference, such as {{ files['three'] }}. Your own code never moves into a separate file.
- The template must render a graceful, well-designed empty state when plays is empty and when any list it uses has no entries.

## Documents in this series

Every document is public, plain markdown, and self contained. Read the one that matches the task instead of guessing a rule:

- [Start here](https://marketplace.rafflex.io/docs/how-to-create.md): What Rafflex is, the two asset types and when to build each, the creator workflow, and the sandbox rules.
- [Connect your AI](https://marketplace.rafflex.io/docs/how-to-create/connect.md): The remote MCP server: connecting each AI client, signing in, starter prompts, watch mode, uploads per client, what a connected AI can and cannot do, and disconnecting.
- [Architect a game](https://marketplace.rafflex.io/docs/how-to-create/games.md): The complete specification for building a marketplace game: data contract, plays array, injection pattern, and definition of done.
- [Architect a block](https://marketplace.rafflex.io/docs/how-to-create/blocks.md): The complete specification for building a marketplace page block: data contract, theming from settings, and definition of done.
- [Test and debug](https://marketplace.rafflex.io/docs/how-to-create/testing.md): The preview scenarios that exercise every template state, testing with a connected AI, and the platform errors mapped to fixes.
- [List and submit](https://marketplace.rafflex.io/docs/how-to-create/submit.md): Creating the product, the listing fields, pricing and creator earnings, the Ready to submit checklist, the reviewer pitfalls, and what review checks.
- [After launch](https://marketplace.rafflex.io/docs/how-to-create/after-launch.md): New versions, release channels, how installs update, install notes, bug reports, questions, what a connected AI reports, and announcements.
- [Template data contract](https://marketplace.rafflex.io/docs/template-context.md): every variable a template receives, with generated samples.