Tired of spending days on prototypes that should take hours? Same here.
I built this Google Memery demo specifically for the Android FF team in Spain to showcase just how accelerated modern development is when leveraging AI. No complex configurations, no paid subscriptions—just clean, functional code generated entirely using the free tier of Gemini.
What traditionally took me a couple of days to build manually was wrapped up here in barely a few hours. Extreme speed and simplicity were the main features here.
Grab the code:
<div id="google-memory-game-container">
<style>
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap');
#google-memory-game-container {
--google-blue: #4285f4;
--google-red: #ea4335;
--google-yellow: #fbbc05;
--google-green: #34a853;
--gray-100: #f8f9fa;
--gray-200: #e8eaed;
--gray-700: #3c4043;
--gray-600: #5f6368;
font-family: 'Roboto', 'Google Sans', Arial, sans-serif;
background-color: #ffffff;
padding: 40px 20px;
border-radius: 24px;
max-width: 720px;
margin: 0 auto;
user-select: none;
color: var(--gray-700);
}
.game-header {
text-align: center;
margin-bottom: 30px;
}
.game-header h2 {
font-weight: 400;
font-size: 28px;
margin: 0;
letter-spacing: -0.5px;
}
.stats-info {
display: flex;
justify-content: center;
gap: 24px;
margin-top: 20px;
color: var(--gray-600);
font-size: 14px;
font-weight: 500;
}
.memory-grid {
display: grid;
grid-template-columns: repeat(5, 1fr);
grid-template-rows: repeat(4, 1fr);
gap: 12px;
perspective: 1000px;
margin: 30px auto;
max-width: 550px;
}
.memory-card {
aspect-ratio: 1 / 1;
position: relative;
transform-style: preserve-3d;
transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
cursor: pointer;
}
.memory-card:hover:not(.flipped):not(.matched) {
transform: translateY(-2px);
}
.memory-card.flipped, .memory-card.matched {
transform: rotateY(180deg);
}
.card-face {
position: absolute;
width: 100%;
height: 100%;
backface-visibility: hidden;
display: flex;
align-items: center;
justify-content: center;
border-radius: 16px;
box-sizing: border-box;
transition: box-shadow 0.3s ease;
}
.card-back {
background-color: var(--google-green);
border: 1px solid rgba(255,255,255,0.3);
color: #000000;
font-size: 2rem;
font-weight: 700;
box-shadow: 0 1px 2px 0 rgba(60,64,67,0.3), 0 1px 3px 1px rgba(60,64,67,0.15);
}
.card-front {
background-color: #ffffff;
transform: rotateY(180deg);
border: 1px solid var(--gray-200);
box-shadow: 0 1px 2px 0 rgba(60,64,67,0.1), 0 1px 3px 1px rgba(60,64,67,0.05);
}
.card-front img {
width: 45%;
height: 45%;
object-fit: contain;
}
.memory-card.matched .card-front {
border: 1px solid var(--google-green);
background-color: #f8fff9;
}
.reset-btn {
display: inline-block;
margin: 0 auto;
padding: 10px 24px;
background-color: var(--google-blue);
color: white;
border: 1px solid transparent;
border-radius: 4px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: background-color 0.2s, box-shadow 0.2s;
}
.reset-btn:hover {
background-color: #1a73e8;
box-shadow: 0 1px 3px 0 rgba(66,133,244,0.45);
}
.btn-container {
display: flex;
justify-content: center;
margin-top: 30px;
}
#victory-overlay {
display: none;
position: fixed;
top: 0; left: 0; width: 100%; height: 100%;
background: rgba(255,255,255,0.98);
z-index: 1000;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
}
@media (max-width: 600px) {
.memory-grid {
grid-template-columns: repeat(4, 1fr);
max-width: 100%;
}
}
</style>
<div class="game-header">
<h2>
<span style="color:var(--google-blue)">G</span><span style="color:var(--google-red)">o</span><span style="color:var(--google-yellow)">o</span><span style="color:var(--google-blue)">g</span><span style="color:var(--google-green)">l</span><span style="color:var(--google-red)">e</span>
<span style="margin-left:8px; color:var(--gray-600)">Memory</span>
</h2>
<div class="stats-info">
<span>Intentos: <span id="moves-val">0</span></span>
<span>Completado: <span id="matches-val">0</span>/10</span>
</div>
</div>
<div class="memory-grid" id="main-grid"></div>
<div class="btn-container">
<button class="reset-btn" onclick="startGame()">Reiniciar juego</button>
</div>
<div id="victory-overlay">
<h1>¡Buen trabajo!</h1>
<p id="victory-stats" style="color:var(--gray-600); margin-bottom: 30px;"></p>
<button class="reset-btn" onclick="startGame()">Jugar de nuevo</button>
</div>
<script>
const IMAGE_URLS = [
"https://www.gstatic.com/marketing-cms/assets/images/66/ac/14b165e647fd85c824bfbe5d6bc5/gmail.webp=s48-fcrop64=1,00000000ffffffff-rw",
"https://www.gstatic.com/marketing-cms/assets/images/cf/3c/0d56042f479fac9ad22d06855578/calender.webp=s48-fcrop64=1,00000000ffffffff-rw",
"https://www.gstatic.com/marketing-cms/assets/images/25/5f/c96350b5427fbfdbdef268515872/google-chat.webp=s48-fcrop64=1,00000000ffffffff-rw",
"https://www.gstatic.com/marketing-cms/assets/images/e8/4f/69d708b2455397d7b88b0312f7c5/google-drive.webp=s48-fcrop64=1,00000000ffffffff-rw",
"https://www.gstatic.com/images/branding/product/2x/googleg_96dp.png",
"https://www.gstatic.com/marketing-cms/assets/images/ef/8c/be724dfe44f88ea9f229c060dd0d/chrome-dino.webp=n-w48-h51-fcrop64=1,00000980fffff700-rw",
"https://www.gstatic.com/marketing-cms/assets/images/aa/d2/356851cc4224bc17a230a98e6000/chrome.webp=s48-fcrop64=1,00000000ffffffff-rw",
"https://www.gstatic.com/marketing-cms/assets/images/e4/09/c1fe2731462599695f4bf3680173/andriod.webp=s48-fcrop64=1,00000000ffffffff-rw",
"https://www.gstatic.com/marketing-cms/assets/images/1a/9f/7bba006e49de847508df3a4a7b93/andriod-full-body.webp=s48-fcrop64=1,00000000ffffffff-rw",
"https://www.gstatic.com/marketing-cms/assets/images/47/53/1b0e060946bf861e3750bf7d7f3f/andriod-auto.webp=s48-fcrop64=1,00000000ffffffff-rw"
];
let flippedCards = [];
let moves = 0;
let matchedCount = 0;
let isLocked = false;
function startGame() {
const grid = document.getElementById('main-grid');
grid.innerHTML = '';
flippedCards = [];
moves = 0;
matchedCount = 0;
isLocked = false;
document.getElementById('moves-val').innerText = '0';
document.getElementById('matches-val').innerText = '0';
document.getElementById('victory-overlay').style.display = 'none';
const deck = [...IMAGE_URLS, ...IMAGE_URLS];
deck.sort(() => Math.random() - 0.5);
deck.forEach((url) => {
const card = document.createElement('div');
card.className = 'memory-card';
card.dataset.url = url;
card.innerHTML = `
<div class="card-face card-back">?</div>
<div class="card-face card-front">
<img src="${url}" alt="Icon" loading="lazy" onerror="this.src='https://www.gstatic.com/images/branding/product/2x/googleg_48dp.png'">
</div>
`;
card.addEventListener('click', flipCard);
grid.appendChild(card);
});
}
function flipCard() {
if (isLocked || this.classList.contains('flipped') || this.classList.contains('matched')) return;
this.classList.add('flipped');
flippedCards.push(this);
if (flippedCards.length === 2) {
moves++;
document.getElementById('moves-val').innerText = moves;
checkMatch();
}
}
function checkMatch() {
isLocked = true;
const [card1, card2] = flippedCards;
const isMatch = card1.dataset.url === card2.dataset.url;
if (isMatch) {
card1.classList.add('matched');
card2.classList.add('matched');
matchedCount++;
document.getElementById('matches-val').innerText = matchedCount;
flippedCards = [];
isLocked = false;
if (matchedCount === 10) setTimeout(showVictory, 600);
} else {
setTimeout(() => {
card1.classList.remove('flipped');
card2.classList.remove('flipped');
flippedCards = [];
isLocked = false;
}, 800);
}
}
function showVictory() {
document.getElementById('victory-stats').innerText = `Has completado el tablero en ${moves} intentos.`;
document.getElementById('victory-overlay').style.display = 'flex';
}
window.onload = startGame;
</script>
</div>
