Overview
This tutorial walks you through building a simple web application that generates 360° skyboxes using the Skybox AI API. You'll learn the core concepts and build a working prototype in under 30 minutes.
What You'll Build:
- A simple web form for text prompt input
- API integration with proper authentication
- Generation status tracking
- Display of the completed skybox
Prerequisites:
- Basic JavaScript/Node.js knowledge
- A Skybox AI account with API access
- Node.js installed on your machine
Step 1: Get Your API Key (5 minutes)
- Log in to skybox.blockadelabs.com
- Navigate to your API settings https://skybox.blockadelabs.com/api
- Create and copy your API key (keep this secure!)
Important: Never expose your API key in client-side code. Always use it server-side.
Step 2: Set Up Your Project (5 minutes)
Create a new directory and initialize your project:
mkdir skybox-demo cd skybox-demo npm init -y npm install express axios dotenv
Create a .env file for your API key:
SKYBOX_API_KEY=your_api_key_here
Security Note: Add .env to your .gitignore file immediately.
Step 3: Create Your Backend (10 minutes)
Create server.js:
require('dotenv').config();
const express = require('express');
const axios = require('axios');
const app = express();
app.use(express.json());
app.use(express.static('public'));
const SKYBOX_API = 'https://backend.blockadelabs.com/api/v1';
const API_KEY = process.env.SKYBOX_API_KEY;
// Generate skybox endpoint
app.post('/api/generate', async (req, res) => {
try {
const { prompt, styleId } = req.body;
const response = await axios.post(
`${SKYBOX_API}/skybox`,
{
prompt: prompt,
skybox_style_id: styleId || 1
},
{
headers: {
'x-api-key': API_KEY,
'Content-Type': 'application/json'
}
}
);
res.json(response.data);
} catch (error) {
console.error('Generation error:', error.response?.data || error.message);
res.status(500).json({
error: 'Failed to generate skybox',
details: error.response?.data
});
}
});
// Check status endpoint
app.get('/api/status/:id', async (req, res) => {
try {
const response = await axios.get(
`${SKYBOX_API}/imagine/requests/${req.params.id}`,
{
headers: {
'x-api-key': API_KEY
}
}
);
res.json(response.data);
} catch (error) {
console.error('Status check error:', error.response?.data || error.message);
res.status(500).json({ error: 'Failed to check status' });
}
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});Step 4: Create Your Frontend (10 minutes)
Create a public directory, then create public/index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Skybox AI Demo</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 50px auto;
padding: 20px;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
input, select, button {
width: 100%;
padding: 10px;
font-size: 16px;
}
button {
background-color: #007bff;
color: white;
border: none;
cursor: pointer;
margin-top: 10px;
}
button:hover {
background-color: #0056b3;
}
button:disabled {
background-color: #ccc;
cursor: not-allowed;
}
#status {
margin-top: 20px;
padding: 15px;
border-radius: 5px;
}
.info { background-color: #d1ecf1; }
.success { background-color: #d4edda; }
.error { background-color: #f8d7da; }
#result {
margin-top: 20px;
}
#skybox-image {
width: 100%;
border-radius: 10px;
}
</style>
</head>
<body>
<h1>Skybox AI Generator</h1>
<div class="form-group">
<label for="prompt">Scene Description:</label>
<input
type="text"
id="prompt"
placeholder="e.g., A futuristic city at sunset with neon lights"
>
</div>
<div class="form-group">
<label for="style">Style:</label>
<select id="style">
<option value="1">Fantasy Landscape</option>
<option value="2">Sci-Fi</option>
<option value="3">Modern Interior</option>
<option value="10">Digital Painting</option>
</select>
</div>
<button id="generateBtn" onclick="generateSkybox()">Generate Skybox</button>
<div id="status"></div>
<div id="result"></div>
<script>
let checkInterval;
async function generateSkybox() {
const prompt = document.getElementById('prompt').value;
const styleId = document.getElementById('style').value;
const statusDiv = document.getElementById('status');
const resultDiv = document.getElementById('result');
const generateBtn = document.getElementById('generateBtn');
if (!prompt) {
alert('Please enter a scene description');
return;
}
// Reset UI
resultDiv.innerHTML = '';
statusDiv.className = 'info';
statusDiv.textContent = 'Submitting request...';
generateBtn.disabled = true;
try {
// Submit generation request
const response = await fetch('/api/generate', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ prompt, styleId: parseInt(styleId) })
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error || 'Generation failed');
}
// Start checking status
checkStatus(data.obfuscated_id);
} catch (error) {
statusDiv.className = 'error';
statusDiv.textContent = `Error: ${error.message}`;
generateBtn.disabled = false;
}
}
async function checkStatus(skyboxId) {
const statusDiv = document.getElementById('status');
const resultDiv = document.getElementById('result');
const generateBtn = document.getElementById('generateBtn');
statusDiv.className = 'info';
statusDiv.textContent = 'Generation in progress...';
checkInterval = setInterval(async () => {
try {
const response = await fetch(`/api/status/${skyboxId}`);
const data = await response.json();
if (data.status === 'pending' || data.status === 'dispatched') {
statusDiv.textContent = `Status: ${data.status}... Please wait.`;
} else if (data.status === 'processing') {
statusDiv.textContent = 'Processing your skybox... Almost there!';
} else if (data.status === 'complete') {
clearInterval(checkInterval);
statusDiv.className = 'success';
statusDiv.textContent = 'Skybox generated successfully!';
resultDiv.innerHTML = `
<h3>Your Skybox:</h3>
<img id="skybox-image" src="${data.file_url}" alt="Generated Skybox">
<p><a href="${data.file_url}" target="_blank">Open Full Size</a></p>
`;
generateBtn.disabled = false;
} else if (data.status === 'error' || data.status === 'abort') {
clearInterval(checkInterval);
statusDiv.className = 'error';
statusDiv.textContent = `Generation failed: ${data.error_message || data.status}`;
generateBtn.disabled = false;
}
} catch (error) {
clearInterval(checkInterval);
statusDiv.className = 'error';
statusDiv.textContent = `Error checking status: ${error.message}`;
generateBtn.disabled = false;
}
}, 3000); // Check every 3 seconds
}
</script>
</body>
</html>Step 5: Run Your Application
Start the server:
node server.js
Open your browser to http://localhost:3000
Understanding the Code
Backend (server.js):
-
/api/generate- Submits a generation request to Skybox AI -
/api/status/:id- Polls for generation status - API key is kept secure on the server side
Frontend (index.html):
- Simple form for prompt and style input
- Submits request via backend
- Polls status every 3 seconds
- Displays completed skybox image
What's Happening Behind the Scenes
- User submits prompt → Frontend sends to your backend
- Backend calls Skybox AI API → Returns generation ID and initial status
- Frontend polls status → Checks every 3 seconds for completion
- Generation completes → Display the skybox image URL
Typical Timeline:
- Queue time: 0-30 seconds (depends on traffic)
- Generation time: 25-45 seconds
- Total: ~30-75 seconds
Common Issues and Solutions
"Failed to generate skybox"
- Check your API key is correct in
.env - Verify you have available generations in your quota
- Check the console for detailed error messages
Status stuck on "pending"
- Normal during high traffic periods
- The queue system is working as expected
- Generation will process when capacity available
"Rate limit exceeded"
- You've hit your generations per minute limit
- Wait for the next minute window
- Consider upgrading your plan
"Cannot GET /"
- Make sure you created the
publicdirectory - Verify
index.htmlis inside thepublicfolder - Check that
app.use(express.static('public'));is in yourserver.js
Next Steps
Now that you have a working integration, consider:
Enhanced Integration Guide - Check out our follow-up guide for more enhanced integrations
- Enhance Prompt and Strict Prompt - Moderate customer prompt requests.
- Style selection - Fetch styles via API and populate dropdown
- Image caching - Store generated skyboxes locally
- Remix functionality - Allow users to remix existing generations
- Export options - Add buttons for different export formats
Additional Resources
- Full API Documentation: api-documentation.blockadelabs.com
- Service Status: status.blockadelabs.com
Congratulations! You've built your first Skybox AI integration. This foundation can be expanded into full production applications.
Related to:
Articles in this section
- Skybox AI Quick Start: Introduction To Building Application With Skybox AI
- Skybox AI Quick Start: Building Your First Integration
- Reimagining Photospheres with Remix in Skybox AI
- Introduction to Skybox AI
- Skybox AI Account Creation and Login
- Skybox AI Workspace and Interface Overview
- Creating Your First Skybox in Skybox AI
- Remixing your Skyboxes in Skybox AI
- Saving and Managing Projects