Remix and attribution for user content
3 min read
A content feed is good. A content movement is better. The difference is remixing: one player builds a clever level, another forks it, tweaks the hard part, and ships their own, with credit flowing back to the original. That's how a handful of creators becomes a family tree of thousands. The scary part is attribution: track it client-side and someone will strip the credit. It has to be enforced on the server.
It is.
Fork with lineage attached
tg.ugc.remix takes a public, remix-allowed item and forks it into your own draft: a full copy of the payload you can edit freely. The lineage link is recorded server-side, so the "remixed from" relationship isn't something a client can fake or erase.
const { item } = await tg.ugc.remix(originalId);
// item is YOUR draft, prefilled with the original's payload
item.payload.difficulty = 'nightmare';
await tg.ugc.update(item.id, { title: 'Sky Bridge Sprint - Nightmare', payload: item.payload });
await tg.ugc.submit(item.id); // goes through moderation like any publish
Note that a remix is a normal draft: it still passes through submit and moderation before it's public, and the original must have been created with allow_remix: true for the fork to be permitted.
Render the credit and the tree
tg.ugc.lineage gives you the whole family: the direct parent, the root of the tree, and every downstream remix. That's everything you need to render "remixed from X" on a level page and a remix count on a creator's original.
const { remix_of, root_id, remixes } = await tg.ugc.lineage(item.id);
if (remix_of) {
const parent = await tg.ugc.get(remix_of);
render(`Remixed from ${parent.item.title}`, `/level/${remix_of}`);
}
// On an original, show how far it spread:
const { remixes: children } = await tg.ugc.lineage(originalId);
render(`${children.length} remixes`);
Surfacing both directions is what makes the loop visible: a remixer sees who they're building on, and an original creator sees their idea spreading. That visible credit is the incentive that keeps people remixing instead of silently copy-pasting.
Pair it with a per-item record board
Lineage tells you who forked what. To make each level competitive, attach a record, like a speedrun best time, keyed to that specific item with tg.keyedBoards. The key is the item id, so every remix carries its own record independent of its parent.
// Player finishes a level in 41.2s:
await tg.keyedBoards.submit('level_best_time', item.id, 41200, {
entryMeta: { player: tg.playerId, replay: replayId },
});
// The current record for this exact level:
const { entry } = await tg.keyedBoards.entry('level_best_time', item.id);
render(`Record: ${(entry.score / 1000).toFixed(1)}s`, entry.entry_meta);
Now a remix isn't just a copy: it's a fresh leaderboard someone can be the first to conquer, which is its own reason to fork.
Going further
Lower-is-better records like time sort ascending, so compare scores yourself when you show a "new record" prompt. Use entry_meta to stash a replay id or ghost data so a record is provable, not just a number. And walk root_id to build a "family tree" view: grouping every descendant of one original is a great creator-spotlight feature.
Takeaway
Server-side lineage makes credit un-strippable, and a per-item keyed board makes every fork worth conquering. Together they turn "here's a level" into a living, competitive tree of creators building on each other.