It’s December again, which means Christmas is right around the corner! If you’re looking for the perfect activity to play with your team this holiday season, we’ve got you covered with our latest creation, and we bet your team will love it too!
Secret Santa has always been a classic festive game, but we’ve added a tech twist to make it even more exciting. No more worrying about picking your own name or having to start over again from the beginning.
This just in — the Educative team has coded Secret Santa and turned it into an engaging game for you to try at your workplace, with no technicalities required. Read the game rules below, and you’ll have everything you need to try the game yourself!
Add your team members’ names when prompted by the first screen.
Don’t repeat names, you can use full names to differentiate if needed. The interface provides the flexibility to edit and delete names too!
Once you’ve added at least two names, you can proceed with the game.
Just add your name in the input, without anyone else seeing the screen, and learn who you’ll be gifting this Christmas.
Remember to clear the screen before you pass it to the next person.
Large touch targets and 44px min tap areas; test on iOS/Android.
Keyboard-only flow: every action reachable via Tab/Enter/Escape; focus states visible.
Color contrast: meet WCAG AA; don’t use color alone to convey status.
Clear errors: explain what failed (e.g., “No valid assignment with current exclusions. Try splitting into two groups.”).
These small changes make your secret Santa generator feel professional.
For those of you who want to learn how to code this Secret Santa game yourself, this section provides you with a step-by-step walkthrough of the entire code. You’ll be a Secret Santa code expert in no time! The code has been kept simple deliberately so that, even if you’re a beginner, you’ll still be able to code it with us.
The code consists of two main HTML pages, namely, index.html and secretsanta.html.
index.html is responsible for receiving the names of the players and for editing or deleting them.
secretsanta.html is responsible for revealing the Secret Santas to the players.
Let’s go through both of these pages and understand how they achieve the aforementioned functionality.
The HTML for index.html is kept minimal and consists of a few catchy headings along with name-entering guidelines. A list containing the names of the participants is also shown, which is initially empty. Users are shown two buttons, one to facilitate the addition of players and the other to start the game. These buttons are linked to two functions, addPlayer() and startGame(), respectively.
<div id="container"><h1>🎁 Secret Santa Code Edition!🎄</h1><p>Enter the names of all the players (no same names allowed, use full names if needed)</p><input type="text" id="name-entry" placeholder="Enter a name"><div id="names-list"></div><button onclick="addPlayer()">Add Player</button><button onclick="startGame()">Start Game</button><img width = '150px' src="https://www.ninacosford.com/wp-content/uploads/2021/10/IMG_3418.gif" alt="Santa Claus" /></div>
The JavaScript code to achieve the actual functionality is written within the <script> tag.
let players = [];
We begin by declaring an empty array, players, for storing the participants.
Quality-of-life upgrades:
Save/reload: sync players, exclusions, and settings to localStorage; add buttons for Export/Import JSON or CSV.
Groups/subgroups: allow multiple lists in one session (e.g., Engineering, Design), each with its own draw.
Dry run: an admin-only preview that simulates small samples to validate constraints before the real draw (no names revealed).
function addPlayer() {const playerName = document.getElementById('name-entry').value.trim();if (playerName !== '' && !players.includes(playerName)) {players.push(playerName);document.getElementById('name-entry').value = '';updateNamesList();} else {alert('Please enter a valid name (no same names allowed).');}}
Next, we define the addPlayer() function, which is invoked once a user has entered the name of the player in the input box and has clicked on the button. playerName retrieves the inputted name and ensures that it’s neither empty nor one of the already existing players. If the checks are passed, the function pushes the playerName into the players array, clears the user input box, and updates the list to add this name by calling the updateNamesList() function.
function updateNamesList() {const namesListContainer = document.getElementById('names-list');namesListContainer.innerHTML = "<p>Names entered:</p>";const ul = document.createElement('ul');players.forEach((player, index) => {const li = document.createElement('li');li.innerHTML = `<span>${player}</span><button onclick="editName(${index})">Edit</button><button onclick="deleteName(${index})">Delete</button>`;ul.appendChild(li);});namesListContainer.appendChild(ul);}
The updateNamesList() function traverses over the players array using a forEach loop and creates a list element for every player using the <li> tag to display on the screen. With each name, an edit and a delete button are also attached. Clicking on the edit button invokes the editName() function, and the code passes the index of the player array (pointing to the player name) to the function. Similarly, clicking on delete button invokes the deleteName() function along with passing the index.
function editName(index) {const newName = prompt('Enter the new name:');if (newName !== null && newName.trim() !== '') {players[index] = newName.trim();updateNamesList();}}
The editName() function creates a prompt for the user to enter the updated name, and if it’s not empty, the name is successfully updated through players[index] = newName.trim();.
function deleteName(index) {if (confirm('Are you sure you want to delete this name?')) {players.splice(index, 1);updateNamesList();}}
The deleteName() function first confirms whether the user wants to delete the player, as a good usability practice, and if the user offers confirmation, the code removes the name from players using the splice() method.
function startGame() {if (players.length < 2) {alert('Please enter at least two names to start the game.');}else {localStorage.setItem('gamePlayers', JSON.stringify(players));window.location.href = 'secretsanta.html';}}
Finally, the startGame() function is defined. It’s called when the user clicks on the start game button. If at least two players have been added, the game begins on the secretsanta.html page. We are redirected to this page by updating the window.location.href property.
This is the page where the actual fun lies! Let’s see how the game actually works by understanding the code below.
<div id="container"><h1>🎁 Secret Santa Assignments 🎄</h1><p>Enter your name to reveal your assigned giftee!</p><input type="text" id="user-name-entry" placeholder="Enter your name"><button onclick="revealGiftee()">Reveal Giftee</button><button id="clear-button" onclick="clearScreen()">Clear Screen</button><p id="assignments"></p></div>
The HTML part is pretty easy. We just display a user input box here for user name input, against which we’ll reveal the person they have to give a gift to. We also have two buttons — one for revealing the giftee, which calls the revealGiftee() function, and one for clearing the screen, which calls the clearScreen() method.
Not every team can pass a laptop around. Offer both experiences:
Party mode (single device):
Keep your existing flow, plus a large “Hide results” button and an automatic 10-second screen clear.
Optional PIN for the next person so the previous participant can’t peek.
Invite mode (remote):
Admin enters names/emails and sets budget, event date, and notes.
Each participant receives a one-time reveal link (with a claim code) that shows only their giftee and wishlist.
Links expire after reveal; an admin can resend or rotate a single participant if plans change.
Both modes elevate your secret Santa generator from a demo to a production-ready tool for distributed teams.
function revealGiftee() {const userName = document.getElementById('user-name-entry').value.trim();if (userName !== '') {if (!assignmentsMade) {const players = JSON.parse(localStorage.getItem('gamePlayers')) || [];assignments = assignSecretSanta(players);assignmentsMade = true;}const userAssignment = assignments.find(pair => pair[0] === userName);if (userAssignment) {document.getElementById('assignments').innerHTML = `${userName}, your Secret Santa giftee is ${userAssignment[1]}.`;} else {document.getElementById('assignments').innerHTML = `Sorry, ${userName}, your name was not found in the game.`;}} else {alert('Please enter your name!');}}
The revealGiftee() function first retrieves the name from the HTML element and ensures that it isn’t empty. It then checks whether or not the function for assigning all of the people their giftees has been executed. The assignSecretSanta() function performs all of the assignments and it’s meant to be run only once. If it hasn’t been executed yet, we call the function and set the assignmentsMade variable to true. This is because assignments must be consistent and made only once.
We use the userAssignment variable to store the giftee assigned to the current player by using the find() method and passing userName to it. The obtained result (either the giftee if a valid name was entered or an error message) is then displayed on the screen through HTML.
function assignSecretSanta(players) {const shuffledPlayers = players.slice().sort(() => Math.random() - 0.5);const assignments = [];const gifteesAssigned = {};for (let i = 0; i < shuffledPlayers.length; i++) {let gifteeIndex = i;while (shuffledPlayers[i] === shuffledPlayers[gifteeIndex] ||(gifteesAssigned[shuffledPlayers[gifteeIndex]] &&Object.keys(gifteesAssigned).length < shuffledPlayers.length)) {gifteeIndex = Math.floor(Math.random() * shuffledPlayers.length);}assignments.push([shuffledPlayers[i], shuffledPlayers[gifteeIndex]]);gifteesAssigned[shuffledPlayers[gifteeIndex]] = true;}return assignments;}
The assignSecretSanta() function is the essence of the code. This function basically takes an array of players and assigns each player a Secret Santa giftee. We begin by shuffling the players using the Fisher-Yates algorithm (sorting using a random comparator). We then perform the following process for each of the shuffled players using a for loop.
There are two main conditions for the code to generate optimal results:
We must ensure that a person who is already being gifted is not assigned again. This in turn leads to all employees being gifted at least once.
We should also ensure that an employee cannot gift themselves.
A while loop ensures these conditions, and we randomly select a new giftee until a valid assignment is found. Let’s go through the conditions that make this game work. We run the loop until a suitable giftee is found by checking the following functions.
shuffledPlayers[i] === shuffledPlayers[gifteeIndex]: This part checks if the current player is randomly assigned to themselves. We want to ensure that a player doesn't get themselves as a giftee.
(gifteesAssigned[shuffledPlayers[gifteeIndex]] && Object.keys(gifteesAssigned).length < shuffledPlayers.length): This part checks if the randomly selected giftee has already been assigned to another player. It also checks whether the number of unique giftees assigned so far is less than the total number of players. This is done to avoid an infinite loop scenario where it becomes impossible to find a suitable giftee for the current player.
gifteeIndex = Math.floor(Math.random() * shuffledPlayers.length);: If the condition in the while loop is true, it means the current player cannot be assigned the randomly selected giftee. In this case, the process is repeated by assigning a new giftee.
If an optimal giftee is found, the gifter and giftee pair is pushed in the assignments variable and the gifteesAssigned variable for that giftee is set to true in order to keep a track of the giftees — and ensure that others get a chance to be gifted too!
Most people using a secret Santa generator need more than “no self match.” Add first-class constraints:
Relationships and households: never pair partners or people who share an address.
Department exclusions: avoid pairing within the same team if requested.
Prior-year history: import last year’s pairs and exclude repeats for 1–N years.
Hard exclusions: an arbitrary “cannot gift X” list per person.
Implementation sketch:
Model the problem as a bipartite matching between gifters and giftees with forbidden edges.
Build a matrix of allowed edges after applying all constraints.
Run a maximum matching (e.g., Hopcroft–Karp) to get a perfect matching or report that constraints are impossible.
Why this beats random retries: it guarantees either a valid assignment or a fast, clear failure with guidance (e.g., “Relax one exclusion for Dana or split into two groups”).
Mention the secret Santa generator term once or twice here as users will search for constraints specifically.
Your current while-loop can still corner itself on some inputs. For “no-self-only” rules, generate a derangement:
Sattolo’s algorithm produces a single cyclic permutation (good, but not all derangements).
A Fisher–Yates shuffle with swaps to fix fixed points can bias results.
Best: construct a random derangement uniformly (e.g., using subfactorial counting or rejection with a cap) or fall back to bipartite matching for uniformity and constraints.
Rule of thumb:
Only “no self” → uniform derangement is fast and simple.
Any extra rule → bipartite matching for correctness.
Seed the RNG so reruns with the same list and constraints reproduce the same result when needed.
To add the final missing piece to the game, we’ll make sure that no one else gets to know who your Secret Santa is. We achieve this by designing the frontend in such a way that it only tells the specified user who they have to give to, while also allowing you to clear the result before passing it to the next person.
function clearScreen() {document.getElementById('user-name-entry').value = '';document.getElementById('assignments').innerHTML = '';}
To clear the results for the next person, the clearScreen() function comes into play. We initialize the values of the input box and the assignment result to an empty string using ‘’.
And that’s how you code our version of Secret Santa! We’ve covered all there is to know about this HTML, CSS, and JavaScript version of the game.
Protect the fun from edge cases:
Unit tests: verify that no constraints are violated and everyone is assigned exactly once; test 2–500 participants.
Determinism: with a seed, you can reproduce bugs and support a “same result” redraw if the admin only changes cosmetic settings.
Safe redraw: allow rotating a single participant (e.g., someone drops out) by re-running matching on the remaining set while holding all other assignments constant where possible.
Failure guidance: if matching fails, point to the specific exclusion set causing the dead end.
Find the complete code along with the CSS styling below!
Congratulations, you’ve now mastered the code and, in no time, have learned how to create a tech-savvy Secret Santa game! So, what are you waiting for? Get started now and enjoy the festive season with this personalized team activity!
What does the splice() method do in the code?
Make the game useful beyond pairing:
Budget and deadline: store currency, price cap, and exchange date/time; display them on reveal.
Shipping notes: address or delivery preference fields for remote teams.
Wishlists: a short text form and an optional “three ideas” field; show this only to the assigned Santa.
Anonymized Q&A: allow the gifter to send a question that the admin relays without revealing identity.
All of this can be persisted in localStorage for the simple version, or a tiny backend if you add invite mode.