Skip to content

Instantly share code, notes, and snippets.

@kguay
Created February 27, 2026 19:26
Show Gist options
  • Select an option

  • Save kguay/c248b49bb4b4922631a7da18332bd2b8 to your computer and use it in GitHub Desktop.

Select an option

Save kguay/c248b49bb4b4922631a7da18332bd2b8 to your computer and use it in GitHub Desktop.
Google profile icon maker
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.16/dist/tailwind.min.css" rel="stylesheet">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Yantramanav:wght@400;500;700&display=swap" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/fontfaceobserver/2.1.0/fontfaceobserver.standalone.js"></script>
<div id="body" class="bg-gray-100 min-h-screen theme-light">
<div class="container mx-auto px-4 py-12 relative">
<!-- Theme toggle: top-right modern icon -->
<button
id="themeToggleBtn"
type="button"
class="theme-toggle-icon absolute top-4 right-4"
aria-label="Toggle theme"
title="Toggle theme"
>
<!-- Auto (system) icon -->
<span class="theme-icon theme-icon-auto">
<svg viewBox="0 0 24 24" class="w-5 h-5" stroke="currentColor" fill="none" stroke-width="1.5">
<rect x="4" y="5" width="16" height="10" rx="2"></rect>
<line x1="12" y1="15" x2="12" y2="19"></line>
<line x1="8" y1="19" x2="16" y2="19"></line>
</svg>
</span>
<!-- Light icon (clean minimal sun) -->
<span class="theme-icon theme-icon-light hidden">
<svg viewBox="0 0 24 24" class="w-5 h-5" stroke="currentColor" fill="none" stroke-width="1.5">
<circle cx="12" cy="12" r="4"></circle>
<line x1="12" y1="2" x2="12" y2="6"></line>
<line x1="12" y1="18" x2="12" y2="22"></line>
<line x1="2" y1="12" x2="6" y2="12"></line>
<line x1="18" y1="12" x2="22" y2="12"></line>
<line x1="4.5" y1="4.5" x2="6.8" y2="6.8"></line>
<line x1="17.2" y1="17.2" x2="19.5" y2="19.5"></line>
<line x1="4.5" y1="19.5" x2="6.8" y2="17.2"></line>
<line x1="17.2" y1="6.8" x2="19.5" y2="4.5"></line>
</svg>
</span>
<!-- Dark icon -->
<span class="theme-icon theme-icon-dark hidden">
<svg viewBox="0 0 24 24" class="w-5 h-5" stroke="currentColor" fill="none" stroke-width="1.5">
<path d="M21 12.8A9 9 0 1111.2 3a7 7 0 0010 9.8z"></path>
</svg>
</span>
</button>
<h1 class="text-4xl font-bold text-center mb-8 text-gray-800">Profile Icon Generator</h1>
<div class="app-container p-8 mx-auto bg-white shadow-lg rounded-xl">
<div class="grid grid-cols-7 gap-4 mb-8" id="letterPicker"></div>
<br>
<div class="grid grid-cols-7 gap-4 mb-8" id="colorPicker"></div>
<br>
<div class="flex justify-center mb-8">
<canvas id="letterCanvas" width="200" height="200"></canvas>
</div>
<br>
<div class="text-center">
<button id="downloadBtn" class="bg-blue-700 text-white font-bold py-2 px-4 rounded">Download Icon</button>
</div>
</div>
<div class="mt-8 text-center">
<h2 class="text-xl font-bold mb-4 text-gray-700">How to Set Your Google Profile Icon</h2>
<ol class="text-sm list-decimal list-inside text-left mx-auto max-w-xl text-gray-600">
<li>Download the image by clicking the "Download Icon" button.</li>
<li>Sign in to your Google Account.</li>
<li>Open your Google Account settings by clicking your profile picture in the top-right corner and selecting "Manage your Google Account."</li>
<li>Under the "Personal info" tab, click on the profile icon or the current profile picture.</li>
<li>Click on "Set Profile Picture" or "Change" (if you already have a profile picture).</li>
<li>Select the downloaded image from your device and click "Open."</li>
<li>Adjust the cropping if necessary, then click "Done" to save your new profile icon.</li>
</ol>
</div>
</div>
<footer class="text-center text-gray-400 text-sm pb-4">
<p>Created by <a href="https://www.kevinguay.com" target="_blank" class="underline hover:text-gray-900">Kevin Guay</a>. <a href="mailto:iconmaker@kevinguay.com" target="_blank" class="underline hover:text-gray-900">Feedback</a> welcome.</p>
<p>This site runs entirely in your browser — no cookies, no tracking, and no server processing.</p>
</footer>
</div>
<style>
:root {
color-scheme: light;
}
.selected {
border: 1.5px solid white;
}
.app-container {
max-width: 680px;
}
#letterPicker,
#colorPicker {
display: flex;
justify-content: center;
flex-wrap: wrap;
margin-bottom: 20px;
}
.letterSquare,
.colorSquare {
width: 30px;
height: 30px;
margin: 5px;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;
overflow: hidden;
}
.letterSquare {
color: #ffffff;
font-size: 18px;
}
#letterCanvas {
border-radius: 50%;
overflow: hidden;
display: inline-block;
width: 100px;
height: 100px;
}
/* THEME: LIGHT / DARK OVERRIDES */
#body.theme-light {
background-color: #f7fafc; /* bg-gray-100 */
color: #1f2933;
}
#body.theme-dark {
background-color: #111827; /* gray-900-ish */
color: #e5e7eb;
}
#body.theme-dark .app-container {
background-color: #1f2937; /* gray-800 */
color: #e5e7eb;
}
#body.theme-dark h1,
#body.theme-dark h2 {
color: #f9fafb;
}
#body.theme-dark ol,
#body.theme-dark li,
#body.theme-dark p {
color: #d1d5db;
}
#body.theme-dark footer {
color: #9ca3af;
}
#body.theme-dark a {
color: #e5e7eb;
}
#body.theme-dark a:hover {
color: #ffffff;
}
#body.theme-dark .bg-white {
background-color: #1f2937 !important;
}
#body.theme-dark #downloadBtn {
background-color: #2563eb; /* blue-600 */
}
#body.theme-dark #downloadBtn:hover {
background-color: #1d4ed8; /* blue-700 */
}
#body.theme-dark .underline.hover\:text-gray-900:hover {
color: #f9fafb !important;
}
#body.theme-dark {
--tw-ring-color: rgba(59, 130, 246, 0.5);
color-scheme: dark;
}
/* Theme toggle button (PaperMod-style) */
.theme-toggle-btn {
display: inline-flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
border-radius: 9999px;
border: 1px solid #d1d5db; /* gray-300 */
background-color: #ffffff;
cursor: pointer;
box-shadow: 0 1px 2px rgba(0,0,0,0.05);
transition: background-color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease, transform 0.05s ease;
}
.theme-toggle-btn:hover {
background-color: #f3f4f6; /* gray-100 */
}
.theme-toggle-btn:active {
transform: scale(0.96);
}
.theme-icon svg {
display: block;
fill: currentColor;
}
.hidden {
display: none;
}
#body.theme-dark .theme-toggle-btn {
background-color: #111827;
border-color: #4b5563; /* gray-600 */
color: #e5e7eb;
box-shadow: 0 1px 2px rgba(0,0,0,0.4);
}
#body.theme-dark .theme-toggle-btn:hover {
background-color: #1f2937;
}
/* Minimal modern icon-only theme toggle */
.theme-toggle-icon {
background: none;
border: none;
padding: 0;
cursor: pointer;
opacity: 0.65;
transition: opacity 0.15s ease;
}
.theme-toggle-icon:hover {
opacity: 1;
}
.theme-icon svg {
display: block;
stroke: currentColor;
fill: currentColor;
}
/* Optional: adjust icon color in dark mode */
#body.theme-dark .theme-toggle-icon {
color: #e5e7eb; /* gray-200 */
}
#body.theme-light .theme-toggle-icon {
color: #374151; /* gray-700 */
}
</style>
<script>
// THEME HANDLING
const THEME_KEY = 'theme-preference';
const themeToggleBtn = document.getElementById('themeToggleBtn');
const iconAuto = document.querySelector('.theme-icon-auto');
const iconLight = document.querySelector('.theme-icon-light');
const iconDark = document.querySelector('.theme-icon-dark');
function getStoredTheme() {
const stored = localStorage.getItem(THEME_KEY);
return stored || 'auto';
}
function getEffectiveTheme(themeSetting) {
if (themeSetting === 'auto') {
const prefersDark = window.matchMedia &&
window.matchMedia('(prefers-color-scheme: dark)').matches;
return prefersDark ? 'dark' : 'light';
}
return themeSetting;
}
function updateThemeIcon(themeSetting) {
if (!iconAuto || !iconLight || !iconDark || !themeToggleBtn) return;
iconAuto.classList.add('hidden');
iconLight.classList.add('hidden');
iconDark.classList.add('hidden');
if (themeSetting === 'auto') {
iconAuto.classList.remove('hidden');
themeToggleBtn.title = 'Auto (system)';
} else if (themeSetting === 'light') {
iconLight.classList.remove('hidden');
themeToggleBtn.title = 'Light theme';
} else if (themeSetting === 'dark') {
iconDark.classList.remove('hidden');
themeToggleBtn.title = 'Dark theme';
}
}
function applyTheme(themeSetting) {
const effectiveTheme = getEffectiveTheme(themeSetting);
// use #body div instead of <body>, but fall back just in case
const bodyEl = document.getElementById('body') || document.body;
if (bodyEl) {
bodyEl.classList.remove('theme-light', 'theme-dark');
bodyEl.classList.add(effectiveTheme === 'dark' ? 'theme-dark' : 'theme-light');
}
document.documentElement.setAttribute('data-theme', themeSetting);
if (effectiveTheme === 'dark') {
document.documentElement.style.colorScheme = 'dark';
} else {
document.documentElement.style.colorScheme = 'light';
}
updateThemeIcon(themeSetting);
}
function cycleTheme(current) {
if (current === 'auto') return 'light';
if (current === 'light') return 'dark';
return 'auto';
}
// Initialize theme on load
let currentTheme = getStoredTheme();
applyTheme(currentTheme);
// Button click cycles through auto -> light -> dark -> auto
if (themeToggleBtn) {
themeToggleBtn.addEventListener('click', () => {
currentTheme = cycleTheme(currentTheme);
localStorage.setItem(THEME_KEY, currentTheme);
applyTheme(currentTheme);
});
}
// React to system changes when in auto mode
if (window.matchMedia) {
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
mediaQuery.addEventListener('change', () => {
const stored = getStoredTheme();
currentTheme = stored;
if (stored === 'auto') {
applyTheme('auto');
}
});
}
// EXISTING APP LOGIC
const canvas = document.getElementById('letterCanvas');
const ctx = canvas.getContext('2d');
const downloadBtn = document.getElementById('downloadBtn');
const colorPicker = document.getElementById('colorPicker');
const letterPicker = document.getElementById('letterPicker');
const colors = [
'AA47BC', '7A1FA2', '78909C', '465A65', 'EC407A', 'C2175B', '5C6BC0', '0288D1',
'00579C', '0098A6', '00887A', '004C3F', '689F39', '33691E', '8C6E63', '5D4038',
'7E57C2', '512DA7', 'EF6C00', 'F5511E', 'BF360C'
];
const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
const defaultColor = '00579C';
const defaultLetter = 'A';
let selectedColor = defaultColor;
let selectedLetter = defaultLetter;
let selectedColorSquare;
let selectedLetterSquare;
ctx.font = '550px Yantramanav';
ctx.fillStyle = 'white';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
function drawLetter(letter, color) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
canvas.width = 1000;
canvas.height = 1000;
ctx.fillStyle = color;
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'white';
ctx.font = '550px Yantramanav';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
const metrics = ctx.measureText(letter);
const yOffset = metrics.actualBoundingBoxAscent / 2 - metrics.actualBoundingBoxDescent / 2;
ctx.fillText(letter, canvas.width / 2, canvas.height / 2 + yOffset);
}
drawLetter(defaultLetter, `#${defaultColor}`);
function createColorPicker() {
for (const color of colors) {
const colorSquare = document.createElement('div');
colorSquare.classList.add('colorSquare');
colorSquare.style.backgroundColor = `#${color}`;
colorSquare.addEventListener('click', () => {
if (selectedColorSquare) {
selectedColorSquare.classList.remove('selected');
}
selectedColorSquare = colorSquare;
selectedColorSquare.classList.add('selected');
drawLetter(selectedLetter, `#${color}`);
selectedColor = color;
});
if (color === defaultColor) {
selectedColorSquare = colorSquare;
selectedColorSquare.classList.add('selected');
}
colorPicker.appendChild(colorSquare);
}
}
function createLetterPicker() {
for (const letter of letters) {
const letterSquare = document.createElement('div');
letterSquare.classList.add('letterSquare');
letterSquare.style.backgroundColor = `#666`;
letterSquare.textContent = letter;
letterSquare.addEventListener('click', () => {
if (selectedLetterSquare) {
selectedLetterSquare.classList.remove('selected');
}
selectedLetterSquare = letterSquare;
selectedLetterSquare.classList.add('selected');
selectedLetter = letter;
drawLetter(letter, `#${selectedColor}`);
});
if (letter === defaultLetter) {
selectedLetterSquare = letterSquare;
selectedLetterSquare.classList.add('selected');
}
letterPicker.appendChild(letterSquare);
}
}
const fontObserver = new FontFaceObserver('Yantramanav');
fontObserver.load().then(() => {
drawLetter(defaultLetter, `#${defaultColor}`);
createLetterPicker();
}).catch((err) => {
console.error('Error loading Yantramanav font:', err);
});
createColorPicker();
downloadBtn.addEventListener('click', () => {
const link = document.createElement('a');
link.href = canvas.toDataURL('image/png');
link.download = `google-icon-${selectedLetter}.png`;
link.click();
});
const privacyPolicyModal = document.getElementById('privacyPolicyModal');
const closeModal = document.getElementById('closeModal');
const privacyPolicyLink = document.getElementById('privacyPolicyLink');
if (privacyPolicyModal && closeModal && privacyPolicyLink) {
privacyPolicyLink.addEventListener('click', (e) => {
e.preventDefault();
privacyPolicyModal.classList.remove('hidden');
});
closeModal.addEventListener('click', () => {
privacyPolicyModal.classList.add('hidden');
});
window.addEventListener('click', (e) => {
if (e.target === privacyPolicyModal) {
privacyPolicyModal.classList.add('hidden');
}
});
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment