APIs & External Data
Fetch JSON from public APIs, handle async data loading, and drive visuals with live weather, finance, or music data.
The two approaches
loadJSON() in preload — simple, blocking, good for static data files:
let data;
function preload() {
data = loadJSON('https://api.example.com/data.json');
}
fetch() / httpGet() in draw or event handlers — non-blocking, good for live polling or triggered loads:
let weatherData = null;
function setup() {
createCanvas(600, 400);
fetchWeather('London');
}
function fetchWeather(city) {
let url = `https://wttr.in/${city}?format=j1`;
loadJSON(url, function(data) {
weatherData = data;
});
}
The fetch API
Modern JavaScript’s fetch() returns a Promise. Use async/await for readable async code:
async function loadData(url) {
try {
let res = await fetch(url);
let json = await res.json();
return json;
} catch (err) {
console.error('Failed to fetch:', err);
return null;
}
}
function setup() {
createCanvas(700, 500);
loadData('https://api.open-meteo.com/v1/forecast?latitude=51.5&longitude=-0.12¤t_weather=true')
.then(d => {
if (d) weatherData = d.current_weather;
});
}
CORS and proxy issues
Browsers block cross-origin requests by default. If an API doesn’t send Access-Control-Allow-Origin headers:
- Use a proxy server — run a small Node/Express server that fetches and forwards the data
- Use a CORS proxy — services like
https://corsproxy.io/(only for testing, not production) - Use APIs that support CORS — most modern public APIs do
Public APIs for creative use
| API | Data | Needs key? |
|---|---|---|
| Open-Meteo | Weather, forecasts | No |
| wttr.in | Weather | No |
| NASA APIs | Astronomy, ISS, APOD | Free key |
| OpenStreetMap Nominatim | Geocoding | No |
| Wikipedia | Articles, summaries | No |
| USGS Earthquake Feed | Earthquake data | No |
| CoinGecko | Crypto prices | No (limited) |
Earthquake visualiser
let quakes = [];
async function loadQuakes() {
let url = 'https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_day.geojson';
let res = await fetch(url);
let json = await res.json();
quakes = json.features.map(f => ({
mag: f.properties.mag,
lon: f.geometry.coordinates[0],
lat: f.geometry.coordinates[1],
name: f.properties.place
}));
}
function setup() {
createCanvas(800, 500);
loadQuakes();
}
function draw() {
background(10, 20, 40);
// Simple equirectangular map projection
for (let q of quakes) {
let x = map(q.lon, -180, 180, 0, width);
let y = map(q.lat, -90, 90, height, 0);
let r = map(q.mag, 0, 8, 3, 30);
let hot = map(q.mag, 2.5, 8, 0, 1);
fill(lerp(50, 255, hot), lerp(200, 50, hot), lerp(255, 50, hot), 180);
noStroke();
circle(x, y, r * 2);
}
fill(255);
textSize(12);
text(`${quakes.length} earthquakes in the past day`, 10, 20);
}
WebSockets for real-time data
For live streaming data (financial tickers, live sensors), WebSockets provide a persistent connection:
let ws;
let latestPrice = null;
function setup() {
createCanvas(700, 400);
ws = new WebSocket('wss://your-realtime-api.com/stream');
ws.onopen = function() {
ws.send(JSON.stringify({ action: 'subscribe', symbol: 'BTC-USD' }));
};
ws.onmessage = function(event) {
let data = JSON.parse(event.data);
latestPrice = data.price;
};
ws.onerror = function(err) {
console.error('WebSocket error:', err);
};
}
function draw() {
background(20);
if (latestPrice) {
fill(100, 255, 150);
textSize(48);
textAlign(CENTER, CENTER);
text(`$${latestPrice.toFixed(2)}`, width / 2, height / 2);
} else {
fill(100);
textSize(20);
textAlign(CENTER, CENTER);
text('Connecting...', width / 2, height / 2);
}
}
Caching and rate limiting
APIs have rate limits. Cache responses and only refetch when needed:
let cache = {};
let lastFetch = 0;
const CACHE_MS = 60000; // 1 minute
async function fetchWithCache(url) {
let now = Date.now();
if (cache[url] && now - lastFetch < CACHE_MS) {
return cache[url];
}
let res = await fetch(url);
let data = await res.json();
cache[url] = data;
lastFetch = now;
return data;
}
Key takeaways
loadJSON(url, callback)is the p5 way;fetch()withasync/awaitis more flexible- CORS blocks cross-origin requests — use APIs that support it, or proxy through your own server
- Don’t call APIs inside
draw()— trigger fetches from events or intervals - Cache responses and respect rate limits
- WebSockets provide push-based real-time data without polling
- Public APIs (weather, earthquakes, astronomy) are fantastic sources of live data for creative work