Tutorials
Free, Unlimited Cloud Save API for Games
Want to add cloud saves to your game without the hassle of managing servers or databases or paying for proprietary services? With Puter.js, you can save player progress, game states, and more to the cloud for free, without any API keys or usage limits. This tutorial will show you how to use Puter.js to implement a cloud save system for your web games.
Getting Started
Add Puter.js to your game with a single line:
<script src="https://js.puter.com/v2/"></script>
That's it - you're ready to start saving game data to the cloud.
Example 1Simple Save/Load System
Let's start with a basic save/load system for a game:
<html>
<body>
<script src="https://js.puter.com/v2/"></script>
<!-- Simple game UI -->
<div>
<h3>Player Stats</h3>
<p>Gold: <span id="gold">0</span></p>
<p>Level: <span id="level">1</span></p>
<button onclick="addGold()">Get Gold</button>
<button onclick="levelUp()">Level Up</button>
<br><br>
<button onclick="saveGame()">Save Game</button>
<button onclick="loadGame()">Load Game</button>
</div>
<script>
// Game state
let playerData = {
gold: 0,
level: 1,
lastSaved: null
};
// Game actions
function addGold() {
playerData.gold += 10;
updateUI();
}
function levelUp() {
playerData.level += 1;
updateUI();
}
function updateUI() {
document.getElementById('gold').textContent = playerData.gold;
document.getElementById('level').textContent = playerData.level;
}
// Save game data
async function saveGame() {
try {
playerData.lastSaved = new Date().toISOString();
await puter.kv.set('gameData', JSON.stringify(playerData));
alert('Game saved!');
} catch (error) {
alert('Failed to save: ' + error);
}
}
// Load game data
async function loadGame() {
try {
const saved = await puter.kv.get('gameData');
if (saved) {
playerData = JSON.parse(saved);
updateUI();
alert('Game loaded!');
} else {
alert('No saved game found');
}
} catch (error) {
alert('Failed to load: ' + error);
}
}
</script>
</body>
</html>
This first example demonstrates the basics of saving and loading game data. The code maintains a simple game state with gold and level variables, storing them in Puter's key-value store. When a player clicks "Save Game", their current progress is converted to JSON and stored in the cloud with a timestamp. The "Load Game" button retrieves this data and restores their progress.
Key points about this example:
- Game state is kept in a single object, making it easy to save and load
- The
lastSaved
timestamp helps track when the game was last saved - Data is stored using
puter.kv.set()
which handles all the cloud storage details - JSON is used to convert the game state to a format that can be stored
Example 2Auto-Save System
Here's how to implement an auto-save system that saves after important actions:
<html>
<body>
<script src="https://js.puter.com/v2/"></script>
<div>
<h3>RPG Character</h3>
<p>Health: <span id="health">100</span></p>
<p>Experience: <span id="xp">0</span></p>
<button onclick="gainXP()">Gain XP</button>
<button onclick="heal()">Heal</button>
<p id="save-status"></p>
</div>
<script>
let gameState = {
health: 100,
xp: 0
};
let saveTimeout;
const SAVE_DELAY = 2000; // Wait 2 seconds after last action
function updateUI() {
document.getElementById('health').textContent = gameState.health;
document.getElementById('xp').textContent = gameState.xp;
}
function setSaveStatus(message) {
document.getElementById('save-status').textContent = message;
}
// Schedule an auto-save
function scheduleAutoSave() {
clearTimeout(saveTimeout);
setSaveStatus('Waiting to save...');
saveTimeout = setTimeout(async () => {
try {
await puter.kv.set('autoSave', JSON.stringify(gameState));
setSaveStatus('Game saved automatically!');
} catch (error) {
setSaveStatus('Auto-save failed!');
}
}, SAVE_DELAY);
}
// Game actions
function gainXP() {
gameState.xp += 10;
updateUI();
scheduleAutoSave();
}
function heal() {
gameState.health = 100;
updateUI();
scheduleAutoSave();
}
// Load last auto-save when game starts
async function loadLastGame() {
try {
const saved = await puter.kv.get('autoSave');
if (saved) {
gameState = JSON.parse(saved);
updateUI();
setSaveStatus('Previous game loaded!');
}
} catch (error) {
setSaveStatus('Could not load previous game');
}
}
loadLastGame();
</script>
</body>
</html>
The auto-save system builds on the basic save/load functionality by automatically saving after player actions. Instead of saving immediately after every action, it uses a delay timer to prevent excessive saves. This is particularly useful for games where players make frequent actions.
The auto-save implementation demonstrates several important concepts:
- Uses a timeout to batch multiple quick actions into a single save
- Provides visual feedback about the save status
- Automatically loads the previous game state when starting
- Saves after meaningful actions rather than at fixed intervals
This approach is ideal for RPGs, adventure games, or any game where losing progress would be frustrating for players.
Example 3Save Multiple Characters
Here's how to implement multiple character saves:
<html>
<body>
<script src="https://js.puter.com/v2/"></script>
<div>
<h3>Character Selection</h3>
<div id="character-list"></div>
<br>
<button onclick="createCharacter()">New Character</button>
<div id="active-character" style="display: none">
<h3>Playing as: <span id="char-name"></span></h3>
<p>Power: <span id="power">1</span></p>
<button onclick="increasePower()">Train</button>
<button onclick="saveCharacter()">Save</button>
</div>
</div>
<script>
let currentChar = null;
async function loadCharacters() {
const charList = document.getElementById('character-list');
charList.innerHTML = 'Loading characters...';
try {
// Get list of all character saves
const chars = await puter.kv.list('char_*');
if (chars.length === 0) {
charList.innerHTML = 'No characters found';
return;
}
// Create buttons for each character
charList.innerHTML = chars
.map(charKey => {
const name = charKey.replace('char_', '');
return `
<button onclick="loadCharacter('${name}')">
Play as ${name}
</button>
<button onclick="deleteCharacter('${name}')">
❌
</button>
<br><br>
`;
})
.join('');
} catch (error) {
charList.innerHTML = 'Failed to load characters';
}
}
async function createCharacter() {
const name = prompt('Enter character name:');
if (!name) return;
const newChar = {
name: name,
power: 1,
created: Date.now()
};
try {
await puter.kv.set(`char_${name}`, JSON.stringify(newChar));
loadCharacters();
} catch (error) {
alert('Failed to create character');
}
}
async function loadCharacter(name) {
try {
const charData = await puter.kv.get(`char_${name}`);
if (charData) {
currentChar = JSON.parse(charData);
updateCharacterUI();
}
} catch (error) {
alert('Failed to load character');
}
}
function updateCharacterUI() {
const activeDiv = document.getElementById('active-character');
activeDiv.style.display = 'block';
document.getElementById('char-name').textContent = currentChar.name;
document.getElementById('power').textContent = currentChar.power;
}
async function saveCharacter() {
if (!currentChar) return;
try {
await puter.kv.set(`char_${currentChar.name}`, JSON.stringify(currentChar));
alert('Character saved!');
} catch (error) {
alert('Failed to save character');
}
}
function increasePower() {
if (!currentChar) return;
currentChar.power++;
updateCharacterUI();
}
async function deleteCharacter(name) {
if (confirm(`Really delete ${name}?`)) {
await puter.kv.del(`char_${name}`);
loadCharacters();
}
}
// Initialize by loading character list
loadCharacters();
</script>
</body>
</html>
This example shows how to manage multiple save files - in this case, different character saves. It demonstrates a complete character management system where players can create, load, save, and delete different characters. This pattern is common in RPGs and other games where players might want to maintain multiple playthroughs.
The multiple character system showcases several advanced techniques:
- Uses a prefix ('char_') to organize related saves in the key-value store
- Implements a complete UI for character management
- Demonstrates how to list and delete saves
- Shows how to handle active character state separately from saved data
This approach can be adapted for other types of multiple save systems, such as save slots or different game worlds.
Quick Implementation Tips
- Always check for data validity when loading saves - use try/catch blocks and verify data structure.
- Save timestamps with your game data to show players when they last saved.
- For large save files (like complete game worlds), use
puter.fs.write()
instead ofputer.kv
. - Implement auto-saves after important game events, not on a timer.
- Keep save data small by only storing essential information.
Advanced Features
For larger games, consider using these additional features:
// Save file versioning
const saveData = {
version: "1.0",
player: playerState,
timestamp: Date.now()
};
// Compress large saves
const compressed = btoa(JSON.stringify(saveData));
await puter.kv.set('gameSave', compressed);
// Multiple save slots
await puter.kv.set(`save_slot_${slotNumber}`, saveData);
Related
Ready to Build Your First App?
Start creating powerful web applications with Puter.js today!
Get Started Now