- Docs
- What To Build
- Dating Compatibility App
Build a Dating Compatibility App
In this tutorial you will build a zodiac compatibility checker that takes two people's birth data and shows how compatible they are. One HTML file, no build tools, no frameworks.
What you will build
- Two forms for birth data (date, time, city)
- A compatibility score (0-100) with category breakdowns
- Strengths and challenges of the relationship in plain English
The complete code
Create a file called compatibility.html and paste this entire block:
<!DOCTYPE html>
<html>
<head>
<title>Zodiac Compatibility</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: system-ui, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; background: #fafafa; }
h1 { text-align: center; margin-bottom: 8px; }
.subtitle { text-align: center; color: #666; margin-bottom: 24px; }
.people { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 24px; }
.person { background: white; padding: 16px; border-radius: 12px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
.person h3 { margin-bottom: 12px; font-size: 16px; }
label { display: block; font-size: 13px; font-weight: 500; margin-bottom: 4px; color: #444; }
input, select { width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 6px; font-size: 14px; margin-bottom: 10px; }
button.check { display: block; width: 100%; padding: 14px; background: #7c3aed; color: white; border: none; border-radius: 12px; font-size: 16px; font-weight: 600; cursor: pointer; margin-bottom: 24px; }
button.check:hover { background: #6d28d9; }
button.check:disabled { background: #ccc; cursor: not-allowed; }
.result { display: none; }
.score-ring { text-align: center; margin-bottom: 20px; }
.score-ring .number { font-size: 64px; font-weight: 700; color: #7c3aed; }
.score-ring .label { font-size: 14px; color: #666; }
.categories { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-bottom: 20px; }
.cat { background: white; padding: 12px; border-radius: 8px; box-shadow: 0 1px 2px rgba(0,0,0,0.05); }
.cat .name { font-size: 12px; font-weight: 600; text-transform: uppercase; color: #666; }
.cat .val { font-size: 20px; font-weight: 600; color: #333; }
.insights { background: white; padding: 16px; border-radius: 12px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
.insights h3 { margin-bottom: 8px; font-size: 15px; }
.insights ul { padding-left: 20px; margin-bottom: 12px; }
.insights li { font-size: 14px; line-height: 1.6; color: #444; }
.summary { font-size: 15px; line-height: 1.6; padding: 12px; background: #f5f0ff; border-radius: 8px; margin-bottom: 16px; }
.loading { text-align: center; padding: 40px; color: #666; }
@media (max-width: 500px) { .people, .categories { grid-template-columns: 1fr; } }
</style>
</head>
<body>
<h1>Zodiac Compatibility</h1>
<p class="subtitle">Enter two birth charts to see how they match</p>
<div class="people">
<div class="person">
<h3>Person 1</h3>
<label>Birth date</label>
<input type="date" id="p1-date" value="1990-07-15" />
<label>Birth time</label>
<input type="time" id="p1-time" value="14:30" />
<label>City</label>
<select id="p1-city">
<option value="40.7128,-74.006,-4">New York</option>
<option value="34.0522,-118.2437,-7">Los Angeles</option>
<option value="51.5074,-0.1278,0">London</option>
<option value="19.076,72.8777,5.5">Mumbai</option>
<option value="35.6762,139.6503,9">Tokyo</option>
<option value="-33.8688,151.2093,10">Sydney</option>
</select>
</div>
<div class="person">
<h3>Person 2</h3>
<label>Birth date</label>
<input type="date" id="p2-date" value="1992-03-22" />
<label>Birth time</label>
<input type="time" id="p2-time" value="09:00" />
<label>City</label>
<select id="p2-city">
<option value="34.0522,-118.2437,-7">Los Angeles</option>
<option value="40.7128,-74.006,-4">New York</option>
<option value="51.5074,-0.1278,0">London</option>
<option value="19.076,72.8777,5.5">Mumbai</option>
<option value="35.6762,139.6503,9">Tokyo</option>
<option value="-33.8688,151.2093,10">Sydney</option>
</select>
</div>
</div>
<button class="check" onclick="checkCompatibility()">Check Compatibility</button>
<div id="result" class="result"></div>
<script>
const API_KEY = 'YOUR_API_KEY'; // Replace with your key from roxyapi.com/pricing
function parsePerson(prefix) {
const date = document.getElementById(prefix + '-date').value;
const time = document.getElementById(prefix + '-time').value + ':00';
const [lat, lng, tz] = document.getElementById(prefix + '-city').value.split(',');
return {
date: date,
time: time,
latitude: parseFloat(lat),
longitude: parseFloat(lng),
timezone: parseFloat(tz)
};
}
async function checkCompatibility() {
const btn = document.querySelector('.check');
btn.disabled = true;
btn.textContent = 'Calculating...';
const el = document.getElementById('result');
el.style.display = 'block';
el.innerHTML = '<div class="loading">Comparing birth charts...</div>';
const response = await fetch('https://roxyapi.com/api/v2/astrology/compatibility-score', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': API_KEY
},
body: JSON.stringify({
person1: parsePerson('p1'),
person2: parsePerson('p2')
})
});
const data = await response.json();
btn.disabled = false;
btn.textContent = 'Check Compatibility';
const cats = data.categories || {};
el.innerHTML = `
<div class="score-ring">
<div class="number">${data.overallScore}</div>
<div class="label">out of 100</div>
</div>
${data.summary ? '<div class="summary">' + data.summary + '</div>' : ''}
<div class="categories">
${Object.entries(cats).map(([k, v]) =>
'<div class="cat"><div class="name">' + k + '</div><div class="val">' + v + '</div></div>'
).join('')}
</div>
<div class="insights">
${data.strengths && data.strengths.length ? '<h3>Strengths</h3><ul>' + data.strengths.map(s => '<li>' + s + '</li>').join('') + '</ul>' : ''}
${data.challenges && data.challenges.length ? '<h3>Challenges</h3><ul>' + data.challenges.map(c => '<li>' + c + '</li>').join('') + '</ul>' : ''}
</div>
`;
}
</script>
</body>
</html>
Replace YOUR_API_KEY with your key, double-click the file, pick birth data for two people, and click "Check Compatibility."
How it works
Two things happen when you click the button:
1. Build the request body. The parsePerson() function reads the form inputs and creates the object the API expects. The compatibility endpoint needs date, time, latitude, longitude, and timezone for each person. The city dropdown maps city names to coordinates — in a real app you would use a geocoding API or let users type their city.
2. Call the API. A POST request to /api/v2/astrology/compatibility-score sends both people's birth data. The response includes:
overallScore— the headline score (0-100)categories— individual scores for romantic, emotional, intellectual, physical, spiritualstrengths— what works well between these two chartschallenges— potential friction pointssummary— a one-liner describing the relationship
Make it yours
- Add a geocoding input — replace the city dropdown with a text input that geocodes via Google Places or OpenStreetMap Nominatim. This lets users enter any city in the world.
- Show individual charts — call
/api/v2/astrology/natal-chartfor each person and display their Sun, Moon, and Rising signs alongside the compatibility score. - Add synastry details — call
/api/v2/astrology/synastryfor a deeper analysis showing how specific planets in one chart interact with the other. - Vedic compatibility — swap the endpoint to
/api/v2/vedic-astrology/compatibilityfor Gun Milan scoring (out of 36 instead of 100). Popular for Indian matchmaking apps.
Next steps
- Tarot Reading App — build a multi-spread tarot reader
- Western Astrology guide — all astrology endpoints explained
- API Reference — full endpoint schemas