Advanced
Power-user corner: things you can build against PianoKitty. Not needed for normal practice.
Rewards API — serve your own reward images
In game mode the app can show a small reward image after correct chords. Besides the built-in cat photos you can plug in your own HTTP endpoint — any URL that returns JSON pointing at an image. Your kid's drawings, family photos, memes — whatever motivates you.
1. The contract
The app sends a plain GET request to your URL and expects this JSON back:
{ "success": 1, "src": "https://your.host/rewards/cat-042.jpg" } successmust be the number1— the string"1"is rejected.src— absolute URL of the image to display (jpg/png/gif/webp…).- Return a different
srcper call — randomization is your endpoint's job.
Anything else — non-200 status, malformed JSON, success ≠ 1, empty src,
or a response slower than 2 seconds — is silently dropped: practice continues,
just without the image. No error is shown.
2. Requirements
- HTTPS. The app runs on HTTPS, so the browser refuses plain-HTTP endpoints (mixed content).
- CORS. The request comes from your browser, cross-origin. Your endpoint must send
Access-Control-Allow-Origin: *(or the app's origin). - The image URL itself needs no CORS headers — it is loaded with a regular
<img>tag.
3. Minimal implementations
PHP — drop a folder of images next to this script:
<?php
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
$images = glob(__DIR__ . '/rewards/*.{jpg,png,gif,webp}', GLOB_BRACE);
echo json_encode([
'success' => 1,
'src' => 'https://your.host/rewards/' . basename($images[array_rand($images)])
]); Cloudflare Worker — no server needed, free tier is plenty:
const IMAGES = [
'https://your.host/img/1.jpg',
'https://your.host/img/2.jpg',
'https://your.host/img/3.jpg'
];
export default {
fetch() {
const src = IMAGES[Math.floor(Math.random() * IMAGES.length)];
return new Response(JSON.stringify({ success: 1, src }), {
headers: {
'content-type': 'application/json',
'access-control-allow-origin': '*'
}
});
}
}; Sanity-check from a terminal before wiring it into the app:
$ curl -s https://your.host/reward
{"success":1,"src":"https://your.host/rewards/cat-042.jpg"} 4. Use it in the app
- Open Preferences → Rewards.
- Set Reward images to Custom API and paste your endpoint URL.
- Rewards appear in game mode after correct chords. To get an image only every Nth correct chord, enable the milestone option and set the interval — the endpoint is then called only on milestones.
Troubleshooting
- No image, no error — that is the designed failure mode. Open DevTools → Network and watch the request to your endpoint after a correct chord.
- CORS error in the console → the
Access-Control-Allow-Originheader is missing. - Works in curl but not in the app → check HTTPS, CORS, and that
successis a number, not a string. - Images sometimes skipped → your endpoint took longer than the 2-second timeout.