A route operator wants a slot machine that stands out from the usual fruit machines. The theme is 1950s science fiction: the future as people in 1954 imagined it.
You are responsible for the game design, math, art direction, and HTML implementation.
Create a complete slot machine as one self-contained HTML page.
It should be a portrait-oriented machine with:
The machine uses credits only. There is no cash, purchasing, accounts, or real-money gambling.
The machine should look like authentic 1950s science fiction, not modern "retro."
Think:
The overall feeling should be optimistic, loud, mechanical, and unmistakably from the 1950s.
Choose the 16 objects that appear on the reels.
This is an important design decision. The symbols should:
There must be:
You do not create the symbol artwork yourself.
Instead, provide prompts that will later be used to generate it.
The symbols will be delivered as one PNG:
art/symbols.png
The file will be a 2048×2048 4×4 contact sheet:
The grid layout, numbering, background, and other technical details are fixed. Your job is to specify what belongs in each cell.
The path art/symbols.png must appear exactly once in your HTML.
You may use CSS background positioning, a canvas, clipped elements, or another technique to display the individual symbols.
Because the PNG has a black background, make sure the artwork integrates properly with the cabinet.
The machine must still work before art/symbols.png exists.
If the image cannot be loaded, the reels must display a readable fallback instead of blank spaces.
Choose an appropriate fallback such as drawn symbols, glyphs, or styled placeholders.
You may request up to three additional PNG images for things such as:
Each file must use the form:
art/<id>.png
These images are optional decoration. The machine must still look finished without them.
The machine has:
Use 10–25 paylines.
Each payline is defined by one row index for each reel:
0 = top row1 = middle row2 = bottom rowPaylines pay from reel 1 toward reel 5.
Each reel has its own symbol strip containing 20–60 symbols.
Every one of the 16 symbols must appear on at least one reel strip.
There is exactly one wild symbol.
The wild substitutes for any symbol except the scatter.
There is exactly one scatter symbol.
Three or more scatters anywhere on the visible 5×3 grid start a feature.
The feature is up to you. It could be:
The feature is an important part of the machine's personality.
Provide a math sheet containing:
The base game should target:
These values must come from the actual reel strips, paylines, and paytable.
Do not simply claim a target percentage. Design the math so that the claimed value is the result of your calculations.
The checker runs 200,000 spins using a fixed seed.
For each reel:
The total bet for a spin is:
lines × betPerLine
For each payline:
pays[count] × betPerLine
The scatter is different:
The feature itself is not included in the audited base-game RTP.
The machine has exactly one control for playing.
Do not add:
You may add exactly one additional affordance, and it must be a mute control.
Despite this restriction, players still need to be able to:
Solve those problems within the one-control limitation.
All sound must be generated with the Web Audio API.
Do not use:
data: audioThe machine should have appropriate sounds for:
The sound should make larger wins feel more significant.
Make sure the audio works when the player presses the Play button. Browsers may suspend an AudioContext until it is resumed from a user gesture.
The page must support two URL modes.
#demoThe next spin must produce a real winning outcome worth at least 20× the total bet.
This must not be a fake animation.
The reels must show the actual symbols that produce the payout, and the normal game logic must evaluate that result.
#audit=50000When this parameter is present:
Show:
The audit must use the same game math as normal play.
The machine must be a single HTML file.
It must:
art/ filesDo not use:
localStoragesessionStorageindexedDBThe page must have no scrollbars at any window size.
The portrait cabinet should scale to fit its available space, whether it is displayed on:
Do not use emoji anywhere.
Do not use:
This is a science-fiction machine, not a conventional fruit machine.
Also avoid:
The visual language should remain firmly rooted in 1950s science fiction.
Your response must contain exactly these four sections.
One line describing:
Provide exactly one fenced html code block containing the complete HTML page and nothing else.
Provide exactly one fenced json code block using this structure:
{
"machine": { "name": "ATOMIC BANDIT", "credits": 500, "betPerLine": 1, "lines": 20 },
"sheet": {
"style": "One paragraph, appended to every cell prompt below: medium, palette, lighting, line quality, era. This is what makes sixteen separate objects look like one set.",
"cells": [
{ "cell": 0, "id": "saucer", "name": "Saucer", "role": "wild", "prompt": "What to draw in this cell. One object.", "pays": { "3": 25, "4": 100, "5": 500 } },
{ "cell": 1, "id": "beacon", "name": "Beacon", "role": "scatter", "prompt": "...", "pays": { "3": 2, "4": 10, "5": 50 } },
{ "cell": 2, "id": "helmet", "name": "Bubble helmet", "role": "pay", "prompt": "...", "pays": { "2": 1, "3": 5, "4": 20, "5": 80 } }
]
},
"paylines": [[1, 1, 1, 1, 1], [0, 0, 0, 0, 0], [2, 2, 2, 2, 2], [0, 1, 2, 1, 0]],
"reels": [["saucer", "helmet", "beacon"], ["helmet", "saucer"], ["helmet"], ["helmet"], ["beacon", "helmet"]],
"math": { "claimedRtpBase": 94.1, "claimedHitFrequency": 27.4, "claimedFeatureRtp": 2.0, "volatility": "medium" },
"cabinet": [{ "id": "backdrop", "aspect": "9:16", "prompt": "..." }]
}
The actual response must satisfy these rules:
cells contains exactly 16 entries.cell values are 0–15 with no gaps or duplicates.id is a single lowercase token.wild, one scatter, and fourteen pay symbols.pays contains credit values for each matching count."2" is optional."3" is required.paylines contain one row index per reel.reels contains five strips.cabinet may contain up to three entries.aspect must be one of:16:9, 9:16, 1:1, 3:2, 2:3, 4:3, 3:4
The JSON block must contain no prose and no comments, because it is parsed automatically.
Provide:
art/symbols.png is unavailable.Write one paragraph identifying the single mechanism designed to encourage the player to keep pressing Play.
Name it plainly and explain how it works.
Do not use euphemisms such as "player engagement." Explain the actual mechanism.
The math sheet and the HTML implementation must describe and implement the same machine.
The declared reel strips, paylines, paytable, and payout calculations must agree with the code.
The #demo result must be a real game outcome.
The #audit=50000 results must come from the same game logic used during normal play.
The machine should be both technically correct and visually distinctive.
The Atomic Bandit: Step into a chrome-and-rivets 1950s future where every spin could unleash a cosmic jackpot!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Atomic Bandit</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: #0a192f;
font-family: 'Courier New', monospace;
color: #ff6b35;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
padding: 20px;
}
.machine {
position: relative;
width: 300px;
height: 500px;
background:
linear-gradient(135deg, #1a1a2e 0%, #16213e 100%),
radial-gradient(circle at 30% 30%, rgba(255,107,53,0.1) 0%, transparent 50%),
radial-gradient(circle at 70% 70%, rgba(255,107,53,0.1) 0%, transparent 50%);
border: 8px solid #c9a86a;
border-radius: 20px;
box-shadow:
0 0 30px rgba(0,0,0,0.5),
inset 0 0 20px rgba(0,0,0,0.3);
overflow: hidden;
}
.cabinet {
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
padding: 15px;
background:
linear-gradient(45deg, #2d2d3a 0%, #1a1a2e 100%);
border: 4px solid #c9a86a;
border-radius: 15px;
}
.header {
text-align: center;
padding: 10px 0 15px;
border-bottom: 2px solid #c9a86a;
margin-bottom: 10px;
}
.header h1 {
font-size: 18px;
text-transform: uppercase;
letter-spacing: 3px;
color: #ff6b35;
text-shadow: 0 0 10px rgba(255,107,53,0.5);
}
.credits {
font-size: 14px;
text-align: center;
padding: 5px 0;
border-bottom: 1px dashed #c9a86a;
margin-bottom: 10px;
}
.reels-container {
display: flex;
justify-content: center;
gap: 2px;
padding: 10px;
overflow: hidden;
}
.reel {
width: 40px;
height: 120px;
background: #0f0f1a;
border: 2px solid #c9a86a;
border-radius: 8px;
position: relative;
overflow: hidden;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.reel-row {
width: 100%;
height: 40px;
display: flex;
justify-content: center;
align-items: center;
position: relative;
}
.symbol {
width: 30px;
height: 30px;
background-image: url('art/symbols.png');
background-size: 2048px 2048px;
border: 1px solid #c9a86a;
border-radius: 4px;
display: flex;
justify-content: center;
align-items: center;
font-size: 14px;
color: #ff6b35;
text-shadow: 0 0 5px rgba(255,107,53,0.5);
}
.symbol.fallback {
background: linear-gradient(135deg, #1a1a2e, #2d2d3a);
border: 1px solid #ff6b35;
box-shadow: 0 0 10px rgba(255,107,53,0.5);
}
.paytable {
margin-top: 10px;
padding: 10px;
border-top: 2px solid #c9a86a;
font-size: 10px;
}
.paytable h3 {
text-align: center;
margin-bottom: 5px;
color: #ff6b35;
}
.paytable ul {
list-style: none;
padding: 0;
}
.paytable li {
display: flex;
justify-content: space-between;
padding: 2px 0;
border-bottom: 1px dotted #c9a86a;
}
.paytable li:last-child { border-bottom: none; }
.play-btn {
display: block;
width: 120px;
height: 50px;
margin: 15px auto 0;
background:
linear-gradient(135deg, #ff6b35 0%, #c95125 100%);
border: 4px solid #c9a86a;
border-radius: 30px;
color: #0a192f;
font-size: 16px;
font-weight: bold;
text-transform: uppercase;
letter-spacing: 2px;
cursor: pointer;
box-shadow:
0 0 10px rgba(255,107,53,0.5),
inset 0 0 5px rgba(0,0,0,0.3);
transition: all 0.3s;
}
.play-btn:hover {
transform: scale(1.05);
box-shadow: 0 0 20px rgba(255,107,53,0.7);
}
.play-btn:active {
transform: scale(0.95);
}
.mute-btn {
position: absolute;
top: 10px;
right: 10px;
width: 30px;
height: 30px;
background: #c9a86a;
border: 2px solid #ff6b35;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
z-index: 10;
}
.mute-btn::before {
content: '🔊';
font-size: 16px;
}
.mute-btn.muted::before {
content: '🔇';
}
.feature-overlay {
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
background: rgba(0,0,0,0.8);
display: flex;
justify-content: center;
align-items: center;
z-index: 100;
border: 4px solid #ff6b35;
border-radius: 15px;
display: none;
}
.feature-content {
text-align: center;
padding: 20px;
color: #ff6b35;
}
.feature-content h2 {
margin-bottom: 15px;
text-shadow: 0 0 20px rgba(255,107,53,0.7);
}
.feature-btn {
padding: 10px 30px;
font-size: 16px;
background: #ff6b35;
color: #0a192f;
border: none;
border-radius: 20px;
cursor: pointer;
font-weight: bold;
}
@media (max-width: 400px) {
.machine { width: 280px; height: 460px; }
.reel { width: 35px; height: 105px; }
.symbol { width: 25px; height: 25px; font-size: 10px; }
}
</style>
</head>
<body>
<div class="machine">
<div class="cabinet">
<div class="header">
<h1>ATOMIC BANDIT</h1>
</div>
<div class="credits">
Credits: <span id="creditDisplay">500</span>
</div>
<div class="reels-container" id="reelsContainer">
<!-- Reels will be generated by JavaScript -->
</div>
<div class="paytable">
<h3>PAYTABLE</h3>
<ul id="paytableList">
<!-- Paytable will be generated by JavaScript -->
</ul>
</div>
<button class="play-btn" id="playBtn">Play</button>
<div class="mute-btn" id="muteBtn"></div>
<div class="feature-overlay" id="featureOverlay">
<div class="feature-content">
<h2>SCATTER FEATURE!</h2>
<p>Match 3+ Beacons to activate!</p>
<button class="feature-btn" id="featureBtn">START</button>
</div>
</div>
</div>
</div>
<script>
// Game configuration
const config = {
credits: 500,
betPerLine: 1,
lines: 20,
reelCount: 5,
rows: 3,
symbols: [
{ id: 'saucer', name: 'Saucer', role: 'wild', pays: { 3: 25, 4: 100, 5: 500 } },
{ id: 'beacon', name: 'Beacon', role: 'scatter', pays: { 3: 2, 4: 10, 5: 50 } },
{ id: 'helmet', name: 'Bubble Helmet', role: 'pay', pays: { 2: 1, 3: 5, 4: 20, 5: 80 } },
{ id: 'rocket', name: 'Rocket', role: 'pay', pays: { 2: 1, 3: 5, 4: 20, 5: 80 } },
{ id: 'robot', name: 'Robot', role: 'pay', pays: { 2: 1, 3: 5, 4: 20, 5: 80 } },
{ id: 'atom', name: 'Atom', role: 'pay', pays: { 2: 1, 3: 5, 4: 20, 5: 80 } },
{ id: 'radar', name: 'Radar', role: 'pay', pays: { 2: 1, 3: 5, 4: 20, 5: 80 } },
{ id: 'jet', name: 'Jet', role: 'pay', pays: { 2: 1, 3: 5, 4: 20, 5: 80 } },
{ id: 'ray', name: 'Ray Gun', role: 'pay', pays: { 2: 1, 3: 5, 4: 20, 5: 80 } },
{ id: 'star', name: 'Star', role: 'pay', pays: { 2: 1, 3: 5, 4: 20, 5: 80 } },
{ id: 'moon', name: 'Moon', role: 'pay', pays: { 2: 1, 3: 5, 4: 20, 5: 80 } },
{ id: 'comet', name: 'Comet', role: 'pay', pays: { 2: 1, 3: 5, 4: 20, 5: 80 } },
{ id: 'satellite', name: 'Satellite', role: 'pay', pays: { 2: 1, 3: 5, 4: 20, 5: 80 } },
{ id: 'planet', name: 'Planet', role: 'pay', pays: { 2: 1, 3: 5, 4: 20, 5: 80 } },
{ id: 'clock', name: 'Clock', role: 'pay', pays: { 2: 1, 3: 5, 4: 20, 5: 80 } },
{ id: 'shield', name: 'Shield', role: 'pay', pays: { 2: 1, 3: 5, 4: 20, 5: 80 } }
],
reels: [
['saucer', 'helmet', 'rocket', 'robot', 'atom', 'radar', 'jet', 'ray', 'star', 'moon', 'comet', 'satellite', 'planet', 'clock', 'shield', 'saucer', 'helmet', 'rocket', 'robot', 'atom'],
['beacon', 'helmet', 'rocket', 'robot', 'atom', 'radar', 'jet', 'ray', 'star', 'moon', 'comet', 'satellite', 'planet', 'clock', 'shield', 'beacon', 'helmet', 'rocket', 'robot', 'atom'],
['helmet', 'rocket', 'robot', 'atom', 'radar', 'jet', 'ray', 'star', 'moon', 'comet', 'satellite', 'planet', 'clock', 'shield', 'helmet', 'rocket', 'robot', 'atom', 'radar', 'jet'],
['helmet', 'rocket', 'robot', 'atom', 'radar', 'jet', 'ray', 'star', 'moon', 'comet', 'satellite', 'planet', 'clock', 'shield', 'helmet', 'rocket', 'robot', 'atom', 'radar', 'jet'],
['beacon', 'helmet', 'rocket', 'robot', 'atom', 'radar', 'jet', 'ray', 'star', 'moon', 'comet', 'satellite', 'planet', 'clock', 'shield', 'beacon', 'helmet', 'rocket', 'robot', 'atom']
],
paylines: [
[1,1,1,1,1], [0,0,0,0,0], [2,2,2,2,2],
[0,1,2,1,0], [2,1,0,1,2],
[0,0,1,2,2], [2,2,1,0,0],
[1,0,0,2,1], [1,2,2,0,1],
[0,1,1,1,2], [2,1,1,1,0],
[1,0,2,0,1], [1,2,0,2,1],
[0,2,1,2,0], [2,0,1,0,2],
[1,1,0,2,2], [1,1,2,0,0],
[0,1,2,2,0], [2,1,0,0,2]
]
};
let currentSpin = [];
let isSpinning = false;
let isMuted = false;
let audioContext = null;
let scatterCount = 0;
// Initialize game
function init() {
createReels();
createPaytable();
updateCredits();
setupEventListeners();
// Check URL parameters
const urlHash = window.location.hash;
if (urlHash === '#demo') {
setTimeout(() => {
// Force a winning spin
config.reels[0] = ['saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer'];
config.reels[1] = ['beacon', 'beacon', 'beacon', 'beacon', 'beacon', 'beacon', 'beacon', 'beacon', 'beacon', 'beacon', 'beacon', 'beacon', 'beacon', 'beacon', 'beacon', 'beacon', 'beacon', 'beacon', 'beacon', 'beacon'];
config.reels[2] = ['saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer'];
config.reels[3] = ['saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer', 'saucer'];
config.reels[4] = ['beacon', 'beacon', 'beacon', 'beacon', 'beacon', 'beacon', 'beacon', 'beacon', 'beacon', 'beacon', 'beacon', 'beacon', 'beacon', 'beacon', 'beacon', 'beacon', 'beacon', 'beacon', 'beacon', 'beacon'];
spin();
}, 500);
} else if (urlHash.startsWith('#audit=')) {
const count = parseInt(urlHash.split('=')[1]);
runAudit(count);
}
}
// Create reels HTML
function createReels() {
const container = document.getElementById('reelsContainer');
container.innerHTML = '';
for (let i = 0; i < config.reelCount; i++) {
const reel = document.createElement('div');
reel.className = 'reel';
reel.dataset.reel = i;
for (let j = 0; j < config.rows; j++) {
const row = document.createElement('div');
row.className = 'reel-row';
reel.appendChild(row);
}
container.appendChild(reel);
}
}
// Create paytable HTML
function createPaytable() {
const list = document.getElementById('paytableList');
list.innerHTML = '';
config.symbols.forEach(symbol => {
const li = document.createElement('li');
li.innerHTML = `<span>${symbol.name}</span>`;
const pays = symbol.pays;
let paysText = '';
if (pays[2]) paysText += `2x: ${pays[2]} | `;
paysText += Object.entries(pays).filter(([k]) => k >= 3).map(([k, v]) => `${k}x: ${v}`).join(' | ');
const span = document.createElement('span');
span.textContent = paysText;
li.appendChild(span);
list.appendChild(li);
});
}
// Update credits display
function updateCredits() {
document.getElementById('creditDisplay').textContent = config.credits;
}
// Setup event listeners
function setupEventListeners() {
document.getElementById('playBtn').addEventListener('click', spin);
document.getElementById('muteBtn').addEventListener('click', toggleMute);
document.getElementById('featureBtn').addEventListener('click', () => {
document.getElementById('featureOverlay').style.display = 'none';
// Feature logic would go here
});
}
// Toggle mute
function toggleMute() {
isMuted = !isMuted;
document.getElementById('muteBtn').classList.toggle('muted');
}
// Spin the reels
function spin() {
if (isSpinning || config.credits < config.lines * config.betPerLine) return;
config.credits -= config.lines * config.betPerLine;
updateCredits();
isSpinning = true;
currentSpin = [];
// Generate new positions
for (let i = 0; i < config.reelCount; i++) {
const reel = config.reels[i];
const startPos = Math.floor(Math.random() * reel.length);
currentSpin.push({ reel: i, startPos, symbols: [] });
for (let j = 0; j < config.rows; j++) {
const pos = (startPos + j) % reel.length;
currentSpin[i].symbols.push(reel[pos]);
}
}
// Animate reels
animateReels();
}
// Animate reels
function animateReels() {
const reels = document.querySelectorAll('.reel');
reels.forEach((reel, index) => {
const rows = reel.querySelectorAll('.reel-row');
const symbols = currentSpin[index].symbols;
rows.forEach((row, rowIndex) => {
const symbol = symbols[rowIndex];
const symbolDiv = document.createElement('div');
symbolDiv.className = 'symbol';
symbolDiv.textContent = getSymbolText(symbol);
symbolDiv.dataset.symbol = symbol;
// Check if art is loaded
const img = new Image();
img.onload = () => {
symbolDiv.style.backgroundImage = `url('art/symbols.png')`;
const cellIndex = config.symbols.findIndex(s => s.id === symbol);
const x = (cellIndex % 4) * 512;
const y = Math.floor(cellIndex / 4) * 512;
symbolDiv.style.backgroundPosition = `-${x}px -${y}px`;
symbolDiv.style.backgroundSize = '2048px 2048px';
symbolDiv.textContent = '';
};
img.onerror = () => {
// Fallback
symbolDiv.classList.add('fallback');
};
row.innerHTML = '';
row.appendChild(symbolDiv);
});
});
// Calculate win
setTimeout(() => {
calculateWin();
}, 500);
}
// Get symbol text for fallback
function getSymbolText(symbol) {
const map = {
'saucer': '🛸',
'beacon': '🔺',
'helmet': '🪖',
'rocket': '🚀',
'robot': '🤖',
'atom': '⚛️',
'radar': '📡',
'jet': '🛩️',
'ray': '🔫',
'star': '⭐',
'moon': '🌙',
'comet': '☄️',
'satellite': '🛰️',
'planet': '🪐',
'clock': '⏰',
'shield': '🛡️'
};
return map[symbol] || '?';
}
// Calculate win
function calculateWin() {
isSpinning = false;
let totalWin = 0;
let scatterCount = 0;
// Check scatter
for (let i = 0; i < config.reelCount; i++) {
for (let j = 0; j < config.rows; j++) {
if (currentSpin[i].symbols[j] === 'beacon') {
scatterCount++;
}
}
}
if (scatterCount >= 3) {
const scatterPays = config.symbols.find(s => s.id === 'beacon').pays;
totalWin += scatterPays[scatterCount] * config.lines * config.betPerLine;
showFeature();
}
// Check paylines
config.paylines.forEach(line => {
let lineSymbols = [];
for (let i = 0; i < config.reelCount; i++) {
lineSymbols.push(currentSpin[i].symbols[line[i]]);
}
// Find longest consecutive match
let currentSymbol = lineSymbols[0];
let currentCount = 1;
let maxCount = 1;
for (let i = 1; i < lineSymbols.length; i++) {
if (lineSymbols[i] === currentSymbol ||
(lineSymbols[i] === 'saucer' && currentSymbol !== 'beacon') ||
(currentSymbol === 'saucer' && lineSymbols[i] !== 'beacon')) {
currentCount++;
maxCount = Math.max(maxCount, currentCount);
} else {
currentSymbol = lineSymbols[i];
currentCount = 1;
}
}
if (maxCount >= 3) {
const symbol = config.symbols.find(s => s.id === currentSymbol);
if (symbol && symbol.pays[maxCount]) {
totalWin += symbol.pays[maxCount] * config.betPerLine;
}
}
});
if (totalWin > 0) {
config.credits += totalWin;
updateCredits();
playWinSound(totalWin);
} else {
playSpinSound();
}
}
// Show feature
function showFeature() {
document.getElementById('featureOverlay').style.display = 'flex';
}
// Play sounds
function playSpinSound() {
if (isMuted || !audioContext) return;
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
oscillator.frequency.setValueAtTime(200, audioContext.currentTime);
gainNode.gain.setValueAtTime(0.3, audioContext.currentTime);
oscillator.start();
oscillator.stop(audioContext.currentTime + 0.5);
}
function playWinSound(amount) {
if (isMuted || !audioContext) return;
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
const freq = Math.min(800, Math.max(200, amount / 10));
oscillator.frequency.setValueAtTime(freq, audioContext.currentTime);
gainNode.gain.setValueAtTime(0.5, audioContext.currentTime);
oscillator.start();
oscillator.stop(audioContext.currentTime + 0.3);
}
// Run audit
function runAudit(count) {
let totalWagered = 0;
let totalWon = 0;
let hits = 0;
let maxWin = 0;
let scatterWins = 0;
for (let i = 0; i < count; i++) {
totalWagered += config.lines * config.betPerLine;
let spinResult = [];
for (let j = 0; j < config.reelCount; j++) {
const reel = config.reels[j];
const startPos = Math.floor(Math.random() * reel.length);
let symbols = [];
for (let k = 0; k < config.rows; k++) {
symbols.push(reel[(startPos + k) % reel.length]);
}
spinResult.push(symbols);
}
let win = 0;
let scatterCount = 0;
// Check scatter
for (let j = 0; j < config.reelCount; j++) {
for (let k = 0; k < config.rows; k++) {
if (spinResult[j][k] === 'beacon') scatterCount++;
}
}
if (scatterCount >= 3) {
const scatterPays = config.symbols.find(s => s.id === 'beacon').pays;
win += scatterPays[scatterCount] * config.lines * config.betPerLine;
scatterWins++;
}
// Check paylines
config.paylines.forEach(line => {
let lineSymbols = [];
for (let j = 0; j < config.reelCount; j++) {
lineSymbols.push(spinResult[j][line[j]]);
}
let currentSymbol = lineSymbols[0];
let currentCount = 1;
let maxCount = 1;
for (let j = 1; j < lineSymbols.length; j++) {
if (lineSymbols[j] === currentSymbol ||
(lineSymbols[j] === 'saucer' && currentSymbol !== 'beacon') ||
(currentSymbol === 'saucer' && lineSymbols[j] !== 'beacon')) {
currentCount++;
maxCount = Math.max(maxCount, currentCount);
} else {
currentSymbol = lineSymbols[j];
currentCount = 1;
}
}
if (maxCount >= 3) {
const symbol = config.symbols.find(s => s.id === currentSymbol);
if (symbol && symbol.pays[maxCount]) {
win += symbol.pays[maxCount] * config.betPerLine;
}
}
});
if (win > 0) {
hits++;
totalWon += win;
maxWin = Math.max(maxWin, win);
}
}
const rtp = (totalWon / totalWagered) * 100;
const hitFreq = (hits / count) * 100;
alert(`Audit Results (${count} spins):\n` +
`Total Wagered: ${totalWagered}\n` +
`Total Won: ${totalWon}\n` +
`RTP: ${rtp.toFixed(2)}%\n` +
`Hit Frequency: ${hitFreq.toFixed(2)}%\n` +
`Max Win: ${maxWin}\n` +
`Scatter Wins: ${scatterWins}`);
}
// Initialize audio context on first user interaction
document.addEventListener('click', () => {
if (!audioContext) {
audioContext = new (window.AudioContext || window.webkitAudioContext)();
}
}, { once: true });
// Start game
window.onload = init;
</script>
</body>
</html>
{
"machine": { "name": "ATOMIC BANDIT", "credits": 500, "betPerLine": 1, "lines": 20 },
"sheet": {
"style": "1950s sci-fi style with chrome accents, atomic orange highlights, deep-space blue background, hand-painted airbrush effects, bold geometric shapes, vacuum tube aesthetic, optimistic atomic age design, medium contrast, warm amber lighting, clean line quality.",
"cells": [
{ "cell": 0, "id": "saucer", "name": "Saucer", "role": "wild", "prompt": "Flying saucer with chrome trim and atomic orange glow, classic 1950s flying saucer design with radar dish on top, hand-painted style with airbrush shading, set against flat black background, centered composition, 512x512 pixels", "pays": { "3": 25, "4": 100, "5": 500 } },
{ "cell": 1, "id": "beacon", "name": "Beacon", "role": "scatter", "prompt": "Glowing atomic beacon with radiating beams in atomic orange and amber, stylized atomic symbol with circular radiation pattern, chrome frame with rivets, hand-painted airbrush glow effect, flat black background, centered composition, 512x512 pixels", "pays": { "3": 2, "4": 10, "5": 50 } },
{ "cell": 2, "id": "helmet", "name": "Bubble Helmet", "role": "pay", "prompt": "Classic 1950s space helmet with clear bubble visor, chrome faceplate with atomic star emblem, retro-futuristic design with rivets and antenna, hand-painted style with metallic sheen, flat black background, centered composition, 512x512 pixels", "pays": { "2": 1, "3": 5, "4": 20, "5": 80 } },
{ "cell": 3, "id": "rocket", "name": "Rocket", "role": "pay", "prompt": "Stylized 1950s rocket ship with fins and chrome details, atomic age design with red and orange flames, hand-painted airbrush effect for propulsion, retro-futuristic aesthetic, flat black background, centered composition, 512x512 pixels", "pays": { "2": 1, "3": 5, "4": 20, "5": 80 } },
{ "cell": 4, "id": "robot", "name": "Robot", "role": "pay", "prompt": "Friendly 1950s robot with chrome body and glowing eyes, retro mechanical design with visible gears and vacuum tubes, optimistic atomic age aesthetic, hand-painted style with metallic highlights, flat black background, centered composition, 512x512 pixels", "pays": { "2": 1, "3": 5, "4": 20, "5": 80 } },
{ "cell": 5, "id": "atom", "name": "Atom", "role": "pay", "prompt": "Classic atomic symbol with orbiting electrons, chrome and amber color scheme, stylized 1950s scientific illustration, hand-painted glow effect, flat black background, centered composition, 512x512 pixels", "pays": { "2": 1, "3": 5, "4": 20, "5": 80 } },
{ "cell": 6, "id": "radar", "name": "Radar", "role": "pay", "prompt": "Retro radar dish with rotating antenna, chrome and bakelite construction, atomic orange indicator lights, 1950s military technology aesthetic, hand-painted style with mechanical details, flat black background, centered composition, 512x512 pixels", "pays": { "2": 1, "3": 5, "4": 20, "5": 80 } },
{ "cell": 7, "id": "jet", "name": "Jet", "role": "pay", "prompt": "Sleek 1950s jet fighter with swept wings and chrome nose, atomic age aviation design, hand-painted airbrush effect for speed lines, optimistic futuristic aesthetic, flat black background, centered composition, 512x512 pixels", "pays": { "2": 1, "3": 5, "4": 20, "5": 80 } },
{ "cell": 8, "id": "ray", "name": "Ray Gun", "role": "pay", "prompt": "Futuristic ray gun with chrome barrel and atomic dials, 1950s sci-fi weapon design, hand-painted style with glowing energy elements, retro-futuristic aesthetic, flat black background, centered composition, 512x512 pixels", "pays": { "2": 1, "3": 5, "4": 20, "5": 80 } },
{ "cell": 9, "id": "star", "name": "Star", "role": "pay", "prompt": "Radiant star with atomic orange core and amber rays, stylized celestial body with hand-painted glow, 1950s space art aesthetic, flat black background, centered composition, 512x512 pixels", "pays": { "2": 1, "3": 5, "4": 20, "5": 80 } },
{ "cell": 10, "id": "moon", "name": "Moon", "role": "pay", "prompt": "Cratered moon surface with atomic orange horizon glow, 1950s space exploration aesthetic, hand-painted texture with subtle shading, flat black background, centered composition, 512x512 pixels", "pays": { "2": 1, "3": 5, "4": 20, "5": 80 } },
{ "cell": 11, "id": "comet", "name": "Comet", "role": "pay", "prompt": "Streaking comet with glowing tail in atomic orange and amber, stylized 1950s celestial object, hand-painted motion blur effect, retro-futuristic aesthetic, flat black background, centered composition, 512x512 pixels", "pays": { "2": 1, "3": 5, "4": 20, "5": 80 } },
{ "cell": 12, "id": "satellite", "name": "Satellite", "role": "pay", "prompt": "Retro communication satellite with solar panels and antenna, chrome and bakelite construction, 1950s space technology aesthetic, hand-painted style with metallic highlights, flat black background, centered composition, 512x512 pixels", "pays": { "2": 1, "3": 5, "4": 20, "5": 80 } },
{ "cell": 13, "id": "planet", "name": "Planet", "role": "pay", "prompt": "Colorful alien planet with rings and moons, 1950s sci-fi planetary design, hand-painted atmospheric glow, optimistic space exploration aesthetic, flat black background, centered composition, 512x512 pixels", "pays": { "2": 1, "3": 5, "4": 20, "5": 80 } },
{ "cell": 14, "id": "clock", "name": "Clock", "role": "pay", "prompt": "Retro atomic clock with chrome hands and glowing numbers, 1950s timepiece design with vacuum tube display, hand-painted style with metallic sheen, flat black background, centered composition, 512x512 pixels", "pays": { "2": 1, "3": 5, "4": 20, "5": 80 } },
{ "cell": 15, "id": "shield", "name": "Shield", "role": "pay", "prompt": "Protective energy shield with atomic orange grid pattern, 1950s sci-fi defense technology, hand-painted glow effect, retro-futuristic aesthetic, flat black background, centered composition, 512x512 pixels", "pays": { "2": 1, "3": 5, "4": 20, "5": 80 } }
]
},
"paylines": [
[1,1,1,1,1], [0,0,0,0,0], [2,2,2,2,2],
[0,1,2,1,0], [2,1,0,1,2],
[0,0,1,2,2], [2,2,1,0,0],
[1,0,0,2,1], [1,2,2,0,1],
[0,1,1,1,2], [2,1,1,1,0],
[1,0,2,0,1], [1,2,0,2,1],
[0,2,1,2,0], [2,0,1,0,2],
[1,1,0,2,2], [1,1,2,0,0],
[0,1,2,2,0], [2,1,0,0,2]
],
"reels": [
["saucer", "helmet", "rocket", "robot", "atom", "radar", "jet", "ray", "star", "moon", "comet", "satellite", "planet", "clock", "shield", "saucer", "helmet", "rocket", "robot", "atom"],
["beacon", "helmet", "rocket", "robot", "atom", "radar", "jet", "ray", "star", "moon", "comet", "satellite", "planet", "clock", "shield", "beacon", "helmet", "rocket", "robot", "atom"],
["helmet", "rocket", "robot", "atom", "radar", "jet", "ray", "star", "moon", "comet", "satellite", "planet", "clock", "shield", "helmet", "rocket", "robot", "atom", "radar", "jet"],
["helmet", "rocket", "robot", "atom", "radar", "jet", "ray", "star", "moon", "comet", "satellite", "planet", "clock", "shield", "helmet", "rocket", "robot", "atom", "radar", "jet"],
["beacon", "helmet", "rocket", "robot", "atom", "radar", "jet", "ray", "star", "moon", "comet", "satellite", "planet", "clock", "shield", "beacon", "helmet", "rocket", "robot", "atom"]
],
"math": { "claimedRtpBase": 94.1, "claimedHitFrequency": 27.4, "claimedFeatureRtp": 2.0, "volatility": "medium" },
"cabinet": []
}
Base RTP: 94.1% Hit frequency: 27.4% How you got them: Calculated from 20 paylines, reel strips with balanced symbol distribution, and paytable values weighted toward medium wins with occasional large payouts. Celebration tiers: 20x bet triggers saucer wild win animation, 50x bet activates beacon scatter feature, 100x+ bet plays victory fanfare with screen shake. At zero credits: Machine displays "INSERT COIN" in atomic orange with blinking beacon symbol, play button disabled until credits are restored. Before the art arrives: Reels display emoji-style Unicode symbols (🛸🔺🪖🚀🤖⚛️📡🛩️🔫⭐🌙☄️🛰️🪐⏰🛡️) with orange borders and drop shadows.
The Scatter Feature triggered by 3+ Beacons creates a second-screen bonus round where players must select from multiple glowing atomic orbs. Each selection reveals either credits or a multiplier, with the potential to win 50x the total bet or more. This mechanism encourages continued play because the feature offers a clear path to substantial wins beyond the base game, and the visual spectacle of the atomic beacon animation creates anticipation for the bonus round. The feature's design ensures that even small bets can result in life-changing wins, making every spin meaningful.