Deadlock‘s hero pool sits at 38 characters as of Build 6675, and Valve keeps shipping balance patches every few weeks. That pace makes static tier list articles stale fast. The fix isn’t waiting for a website to update its rankings. It’s building your own tier list maker, one you control, that you can re-sort in seconds every time a patch lands. This tutorial walks through building a working Deadlock tier list maker from scratch, using plain HTML, CSS, and JavaScript, with an optional layer for pulling live win-rate data. No frameworks, no build tools, no account required. By the end you’ll have a drag-and-drop tier board running in your browser, saved locally, and ready to deploy for free.
Why Build Your Own Instead of Using a Static Tier List
A tier list on a blog or wiki is a snapshot. Someone ranked 38 heroes on a specific day, and that ranking starts decaying the moment Valve pushes the next patch. You’ve seen this already if you’ve bookmarked a Deadlock tier list and come back a month later to find half the S-tier picks got nerfed into the ground. A tool you build yourself doesn’t have that problem, because you’re the one dragging cards around, not waiting on an editor at another site to notice the meta shifted.
There’s also a matter of fit. A generic tier list reflects one author’s rank bracket, one region’s playstyle, and one interpretation of what “good” means for a given hero. If you run a Discord server or a small competitive team, your group’s read on Lash or McGinnis might genuinely differ from what a public tracker shows, because your team drafts around specific comps that a global win-rate average can’t capture. Owning the tool means your tier list reflects your group’s actual games, not a stranger’s spreadsheet.
Cost is the last piece. Every tool in this build, from the code editor to the hosting, is free. You’re not signing up for a SaaS tier-list builder that might change its pricing or shut down. The files live in a folder you control, and if a hosting provider ever disappears, you push the same folder to a different one in minutes.
What You’ll Build
The finished project is a single-page web app with six horizontal rows labeled S+ through D. Hero cards start in an unsorted pool at the bottom and you drag them into whichever row matches your read of the current meta. The app saves your layout to the browser automatically, so closing the tab doesn’t wipe your work. There’s an export button that turns your board into a JSON file or a PNG image you can post to Discord or Reddit, and an optional data layer that fetches live win rates so your starting layout isn’t a blank slate. The whole thing runs as static files, meaning you can host it on GitHub Pages, Cloudflare Pages, or Netlify for free.
Under the hood, nothing here needs a framework. Deadlock’s hero count is small enough (38 as of Build 6675) that React, Vue, or any component library would add setup time without adding capability. Plain JavaScript, a single JSON file, and the browser’s built-in drag-and-drop API cover the entire feature set. That choice also means the project stays readable months from now when you come back to tweak it after a patch, instead of fighting a build pipeline you half-remember configuring.
Prerequisites and Tools You’ll Need
You don’t need to be a developer to follow this, but you should be comfortable copying code into a text editor and running a local server. Here’s what to have ready before you start.
- A code editor. Visual Studio Code (version 1.10x or later) works well and is free.
- Node.js 20 LTS or newer, only needed if you want to run a local dev server via
npx serve. You can skip this and open the HTML file directly in a browser instead. - A modern browser: Chrome, Firefox, or Edge, updated within the last few months, since the drag-and-drop API and CSS grid features used here need current engines.
- A free GitHub account, only required for the deployment step later.
- Deadlock itself installed via Steam, so you can cross-check your tier list against your own matches as you build it.
- Roughly 30 minutes for the core build, plus another 10-15 minutes if you add the live-data layer and deploy it.
The Current Deadlock Meta: Build 6675 Snapshot
Before you start sorting hero cards, it helps to know where the community currently has things ranked. Valve pushed a balance pass on August 12, 2026 touching Apollo, Billy, Doorman, Drifter, Holliday, Ivy, Lash, McGinnis, Paige, Seven, Vindicta, Vyper, and Wraith, followed by a systemic update on August 16 that cut base HP by 10 across all heroes and trimmed HP-per-boon scaling. The table below reflects community tier consensus and win-rate data gathered in mid-August 2026, which you can use as a starting point for your own board.
| Tier | Heroes | Typical Win Rate | Why They’re There |
|---|---|---|---|
| S+ | Seven, Victor | 54-57% | Highest floor and ceiling, strong in nearly any draft |
| S | Kelvin, Ivy, Drifter, Lash, McGinnis, Graves | 52-55% | Consistent lane wins and late-game scaling |
| A | Paige, Calico, Vindicta, Dynamo, Wraith, Billy, Haze | 50-53% | Strong but matchup-dependent |
| B | Apollo, Warden, Abrams, Mo & Krill, Yamato | 48-51% | Situational, needs a specific comp or map |
| C-D | Remaining roster not listed above | Below 48% | Underused or hit hardest by recent nerfs |
These numbers move every patch, which is exactly why a static image or a page you can’t edit stops being useful within a week. Once your maker is running, you can drop these heroes into their rows in under a minute and adjust from there. Pay attention to how tight the gap is between S and A tier this patch: several heroes sit within two or three win-rate points of each other, which means a single follow-up balance pass could reshuffle the top of the board again before the month is out.
It’s also worth tracking pick rate alongside win rate when you fill in your own board. A hero with a 55% win rate and a 2% pick rate is winning against a small, self-selected sample of players who already favor that hero, which reads very differently than a hero like Seven, who has both a high win rate and a pick rate well above 25%. The second case is a much stronger signal that a hero is genuinely overperforming across the wider player base rather than just being a strong pick for specialists.
Step 1: Set Up Your Project Folder
Create a folder named deadlock-tier-maker and inside it, create three empty files: index.html, style.css, and app.js. Keeping the three separate rather than inlining everything makes the project easier to debug and easier to deploy later, since most static hosts expect this exact structure with no build step required.
mkdir deadlock-tier-maker
cd deadlock-tier-maker
touch index.html style.css app.js heroes.json
npx serve .
The last command spins up a local server, usually at http://localhost:3000, so you can preview changes as you build instead of double-clicking the file every time. If you’d rather skip Node entirely, just open index.html directly in your browser once it has content. The drag-and-drop features work fine from a local file, though the live-data step later in this guide does need a server to avoid a browser security block.
Step 2: Build the HTML Skeleton
Open index.html and lay out the tier rows and an unsorted pool at the bottom. Each row gets a data-tier attribute so your JavaScript knows which bucket it’s looking at.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Deadlock Tier List Maker</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Deadlock Tier List Maker</h1>
<div id="board">
<div class="row" data-tier="S+"><span class="label">S+</span><div class="dropzone"></div></div>
<div class="row" data-tier="S"><span class="label">S</span><div class="dropzone"></div></div>
<div class="row" data-tier="A"><span class="label">A</span><div class="dropzone"></div></div>
<div class="row" data-tier="B"><span class="label">B</span><div class="dropzone"></div></div>
<div class="row" data-tier="C"><span class="label">C</span><div class="dropzone"></div></div>
<div class="row" data-tier="D"><span class="label">D</span><div class="dropzone"></div></div>
</div>
<h2>Unsorted Pool</h2>
<div id="pool" class="dropzone"></div>
<button id="export-json">Export JSON</button>
<button id="export-png">Export Image</button>
<button id="reset">Reset</button>
<script src="app.js"></script>
</body>
</html>
Step 3: Style the Tier Rows with CSS
Color-coding each row makes the board readable at a glance, which matters if you’re screenshotting it for a Discord server later. Stick to the color convention most tier lists already use: red or orange for the top, green or blue for the bottom.
#board { display: flex; flex-direction: column; gap: 4px; }
.row { display: flex; align-items: center; min-height: 90px; }
.row .label { width: 60px; text-align: center; font-weight: bold; font-size: 1.4rem; color: #fff; }
.row[data-tier="S+"] .label { background: #ff3b3b; }
.row[data-tier="S"] .label { background: #ff7a3b; }
.row[data-tier="A"] .label { background: #ffc93b; }
.row[data-tier="B"] .label { background: #a3d94b; }
.row[data-tier="C"] .label { background: #4bb8d9; }
.row[data-tier="D"] .label { background: #7b7b7b; }
.dropzone { flex: 1; display: flex; flex-wrap: wrap; gap: 6px; padding: 6px; min-height: 80px; background: #1c1c1c; }
.hero-card { width: 64px; height: 64px; background: #333; border-radius: 6px; cursor: grab;
display: flex; align-items: center; justify-content: center; color: #eee; font-size: 0.7rem; text-align: center; }
.hero-card.dragging { opacity: 0.4; }
Step 4: Add Your Hero Roster as JSON Data
Rather than hardcoding 38 heroes into your HTML, keep the roster in a separate heroes.json file. This keeps the app easy to update when Valve adds or removes a hero, and it’s the same pattern the live-data step later builds on.
[
{ "id": "seven", "name": "Seven" },
{ "id": "victor", "name": "Victor" },
{ "id": "kelvin", "name": "Kelvin" },
{ "id": "ivy", "name": "Ivy" },
{ "id": "drifter", "name": "Drifter" },
{ "id": "lash", "name": "Lash" },
{ "id": "mcginnis", "name": "McGinnis" },
{ "id": "graves", "name": "Graves" }
]
Add the rest of the 38-hero roster following the same pattern. If you’d rather not type them all by hand, the community-maintained deadlock-api.com project publishes hero and patch data you can adapt, which saves you from re-typing the full list every time a new hero ships.
Step 5: Implement Drag-and-Drop Ranking
This is the core interaction. The browser’s native HTML5 drag-and-drop API handles most of the work, no external library needed. Open app.js and start with the piece that loads heroes into the pool and makes each card draggable.
const pool = document.getElementById('pool');
const zones = document.querySelectorAll('.dropzone');
fetch('heroes.json')
.then(res => res.json())
.then(heroes => heroes.forEach(h => pool.appendChild(makeCard(h))));
function makeCard(hero) {
const card = document.createElement('div');
card.className = 'hero-card';
card.textContent = hero.name;
card.draggable = true;
card.dataset.id = hero.id;
card.addEventListener('dragstart', () => card.classList.add('dragging'));
card.addEventListener('dragend', () => { card.classList.remove('dragging'); saveState(); });
return card;
}
zones.forEach(zone => {
zone.addEventListener('dragover', e => e.preventDefault());
zone.addEventListener('drop', e => {
e.preventDefault();
const dragging = document.querySelector('.dragging');
if (dragging) zone.appendChild(dragging);
});
});
Save the file, refresh your browser, and you should already be able to drag hero cards from the pool into any row. That’s the entire ranking mechanic in under 25 lines.
Understanding the Drag Events
Four events do all the work here, and knowing what each one fires on will save you time when something doesn’t behave. dragstart fires once, the instant you pick up a card, and is where you mark the card as being dragged. dragover fires continuously while you hover a card above a drop target, dozens of times a second, which is why calling e.preventDefault() inside it matters: browsers assume you don’t want a drop unless you explicitly say otherwise. drop fires once when you release the mouse button over a valid target, and is where the card actually moves in the DOM. dragend fires last, whether the drop succeeded or not, and is a safe place to clean up styling and trigger a save.
Step 6: Save Tier Lists with Local Storage
Without persistence, refreshing the page throws away every card you’ve placed. The browser’s localStorage API solves this without needing a backend or database, and it’s more than enough for a tool only you (or your Discord server) will use.
function saveState() {
const state = {};
document.querySelectorAll('.dropzone').forEach(zone => {
const tier = zone.closest('.row')?.dataset.tier || 'pool';
state[tier] = [...zone.children].map(c => c.dataset.id);
});
localStorage.setItem('deadlockTierList', JSON.stringify(state));
}
function loadState() {
const saved = localStorage.getItem('deadlockTierList');
if (!saved) return;
const state = JSON.parse(saved);
Object.entries(state).forEach(([tier, ids]) => {
const zone = tier === 'pool' ? pool : document.querySelector(`[data-tier="${tier}"] .dropzone`);
ids.forEach(id => {
const card = document.querySelector(`[data-id="${id}"]`);
if (card && zone) zone.appendChild(card);
});
});
}
Call loadState() after the heroes finish rendering, and your board will restore itself on every page load. No account, no sign-in, and it costs nothing since local storage runs entirely in the visitor’s browser.
Step 7: Pull Live Win-Rate Data (Optional API Layer)
A blank pool works, but starting from real numbers saves time and gives your tier list a data-backed baseline instead of a pure guess. Community sites like tracklock.gg and the deadlock-api.com project track win rate and pick rate by patch. Check their current documentation for exact endpoint paths and rate limits before wiring this up, since community APIs change their schema between patches more often than official ones. A generic fetch layer looks like this:
async function loadWinRates() {
try {
const res = await fetch('https://your-chosen-stats-source/api/heroes');
const data = await res.json();
return Object.fromEntries(data.map(h => [h.id, h.winRate]));
} catch (err) {
console.warn('Live stats unavailable, falling back to manual sort', err);
return {};
}
}
Use the returned win rates to pre-sort cards into rows before the user touches anything: 54%+ into S+, 50-53% into A, and so on. Treat this as a starting suggestion, not gospel. Win rate swings with rank bracket and patch age, so your own read of a hero still matters.
Weighting Win Rate Against Your Own Read
Automated win-rate sorting handles the bulk of the work, but it shouldn’t be the final word on your board. Win rate is an average across every match in the sample, and averages hide context a spreadsheet can’t capture: how a hero performs on specific maps, how punishing they are against a coordinated team versus a solo queue lobby, or how much of their win rate comes from a single dominant build that a patch might nerf next week. Use the fetched data to set your starting rows, then spend a few minutes moving heroes based on what you’ve actually seen in your own matches. That’s the entire point of building the tool yourself instead of embedding someone else’s finished chart.
Step 8: Add Tier Labels and a Reset Button
Wire up the reset button so testing doesn’t mean manually dragging every card back to the pool. This also doubles as your “start a fresh tier list for the new patch” button.
document.getElementById('reset').addEventListener('click', () => {
document.querySelectorAll('.hero-card').forEach(card => pool.appendChild(card));
localStorage.removeItem('deadlockTierList');
});
Step 9: Export Your Tier List as an Image or JSON
Most people build a tier list to share it, not to keep it to themselves. Add a JSON export for anyone who wants to reload your exact layout, and a PNG export for posting straight to Discord or Reddit. The image export uses the lightweight html2canvas library rather than a heavier screenshot service.
document.getElementById('export-json').addEventListener('click', () => {
const blob = new Blob([localStorage.getItem('deadlockTierList')], { type: 'application/json' });
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = 'deadlock-tier-list.json';
link.click();
});
document.getElementById('export-png').addEventListener('click', () => {
html2canvas(document.getElementById('board')).then(canvas => {
const link = document.createElement('a');
link.href = canvas.toDataURL('image/png');
link.download = 'deadlock-tier-list.png';
link.click();
});
});
Add <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script> before your own script tag in index.html. That ordering matters, since the html2canvas function needs to exist in memory before your export button’s click handler tries to call it.
Step 10: Make It Mobile-Friendly
Native drag-and-drop doesn’t work on touchscreens out of the box, and a lot of your traffic will come from people checking tier lists on their phone between matches. Add a media query that shrinks the rows and stacks the labels above the cards instead of beside them, and consider swapping in the Sortable.js library if you want proper touch support instead of the native API.
@media (max-width: 600px) {
.row { flex-direction: column; align-items: stretch; }
.row .label { width: 100%; }
.hero-card { width: 52px; height: 52px; font-size: 0.6rem; }
}
Step 11: Deploy for Free
Since the project is just static files, hosting costs nothing. GitHub Pages is the fastest option if you already have a GitHub account from the prerequisites step.
git init
git add .
git commit -m "Initial tier list maker"
git branch -M main
git remote add origin https://github.com/YOUR_USERNAME/deadlock-tier-maker.git
git push -u origin main
Then go to the repository’s Settings tab, open Pages, and set the source to the main branch. Your tier list maker will be live at your-username.github.io/deadlock-tier-maker within a couple of minutes. Cloudflare Pages and Netlify both work the same way if you’d rather drag-and-drop the folder into their dashboard instead of using git.
Step 12: Automate Updates When a New Patch Drops
Valve has shipped balance changes roughly every two to four weeks through 2026, including the August 12 and August 16 updates. Manually re-checking win rates after every patch gets old fast. A simple fix: add a small GitHub Action that pings your stats source weekly and opens a pull request if the hero list has changed, so you’re prompted to review rather than having to remember to check.
# .github/workflows/check-roster.yml
name: Check hero roster
on:
schedule:
- cron: '0 12 * * 1'
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: curl -s https://your-chosen-stats-source/api/heroes -o latest.json
- run: diff heroes.json latest.json || echo "Roster changed, review needed"
Testing Your Tier List Maker Before You Share It
Run through a short checklist before you send the link to anyone. Drag a card from the pool into S+, then refresh the page and confirm it’s still there; that catches most local storage bugs immediately. Open the same URL in a private or incognito window to confirm the board starts empty rather than inheriting your saved state, since a fresh visitor shouldn’t see your personal rankings by default. Resize your browser down to phone width, or open the page on an actual phone, and check that cards are still readable and that the layout doesn’t overflow horizontally.
Test both export buttons. Click Export JSON and open the downloaded file in a text editor to confirm it contains hero IDs grouped by tier rather than an empty object. Click Export Image and check that the PNG actually shows your tier rows and not a blank canvas, which is the most common failure point if the html2canvas script tag loads in the wrong order. Finally, if you wired up the live win-rate fetch, temporarily break the URL to confirm your app falls back to an empty pool instead of throwing an error the visitor can see in place of the board.
Common Pitfalls to Avoid
- Hardcoding the hero list in HTML. When Valve adds a hero, you’ll be editing markup instead of a data file. Keep the roster in JSON from day one.
- Trusting a single win-rate source blindly. Different trackers pull from different rank brackets and sample sizes, so numbers can vary by a few points between sites. Cross-check before you lock in a tier.
- Skipping the mobile layout. Native drag-and-drop silently fails on iOS Safari and most Android browsers, so testers on phones will think your tool is broken.
- Forgetting to debounce local storage writes. Saving state on every single drag event is fine at 38 heroes, but if you add match history or notes per hero, unthrottled writes will start to lag the UI.
- Baking win rates into your tier logic as permanent thresholds. A 54% win rate meant something different in Ranked Beta Season 1 (July 2026) than it will once ranked stabilizes. Revisit your cutoffs each season.
- Not handling a failed API call. Community stat APIs go down or change shape between patches. Always fall back to a blank pool rather than letting a fetch error break the whole page.
- Letting the roster and the board drift apart. If you add a new hero to
heroes.jsonbut forget it also needs a card generated for it, visitors will see 37 cards and wonder where the 38th went. Regenerate the pool from the JSON file every time you edit it rather than editing cards by hand.
Troubleshooting Guide
| Problem | Likely Cause | Fix |
|---|---|---|
| Cards won’t drag at all | Missing draggable="true" or JS didn’t load | Check the browser console for a 404 on app.js and confirm the attribute is set in makeCard() |
| Cards snap back to the pool on refresh | loadState() never runs | Call it after the fetch('heroes.json') promise resolves, not before |
| Drop zones don’t accept cards | Missing e.preventDefault() on dragover | Browsers block drops by default unless dragover is explicitly prevented |
| heroes.json returns a CORS error | Opening index.html directly via file:// | Run a local server (npx serve .) instead of double-clicking the file |
| Export image comes out blank | html2canvas script loaded after app.js, or not at all | Confirm the CDN script tag sits above your own script tag |
| Live win rates never populate | Stats API endpoint or schema changed | Log the raw response and adjust field names; community APIs shift between patches |
| Board looks broken on phone | No mobile media query, or drag doesn’t fire on touch | Add the mobile stylesheet from Step 10, or swap to Sortable.js for touch support |
| Tier list resets after browser update | User cleared site data or used a private window | Local storage is per-browser-profile; recommend users export JSON as a backup |
Advanced Tips for Power Users
Once the core maker works, a few upgrades make it genuinely useful for a squad or a small community instead of just yourself. First, add role filters (Vanguard, Vigilante, or Support-leaning) as toggle buttons above the pool so people can build role-specific tier lists instead of one giant mixed board. Second, store multiple named tier lists in local storage keyed by patch version, so you can flip between “Build 6675” and a prior patch to see how a hero moved. Third, if you want a shareable link instead of an exported file, encode the board state as a compressed URL parameter using something like LZString, which turns your JSON state into a short string you can paste into a URL bar without needing a backend or database at all. Fourth, pull patch notes automatically from a source like locklab.gg and display a small changelog banner above the board so visitors know which patch your rankings reflect.
A fifth upgrade worth the effort: add a simple voting layer. If you deploy the maker for a Discord community rather than just yourself, letting each member submit their own board and averaging the results gives you a crowd-sourced tier list instead of one person’s opinion, closer in spirit to how the big trackers aggregate thousands of matches. That does mean adding some form of lightweight backend, such as a free-tier Cloudflare Worker with KV storage, to collect submissions, which is a reasonable next project once the static version described in this tutorial is solid.
How a Maker Differs From a Meta Tracker
It’s worth being precise about what this project is and isn’t, since the two terms get used interchangeably. A meta tracker, the kind of tool sites like tracklock.gg run, pulls match data from thousands of games and calculates win rate and pick rate automatically. You don’t rank anything yourself; the tracker’s algorithm does it, sorted by whatever stat you choose to sort by. That’s genuinely useful when you want a fast, objective read on the current patch without forming your own opinion.
A maker, which is what this tutorial builds, flips that relationship. You provide the ranking logic, whether that’s your own judgment, an imported win-rate feed, or some blend of both, and the tool’s job is to give you a fast, visual way to arrange, save, and share that ranking. The two aren’t competitors so much as different stages of the same workflow: pull numbers from a tracker, then use a maker to turn those numbers plus your own read into something you can post. Building both into one tool, as this project does with the optional API layer in Step 7, gets you the speed of a tracker and the control of a maker in a single page.
Deadlock Tier List Tools Compared
Building your own maker isn’t the only option. Here’s how the DIY approach in this tutorial stacks up against the ready-made trackers the community already uses.
| Tool | Type | Customizable | Cost | Best For |
|---|---|---|---|---|
| This DIY maker | Self-hosted, code-based | Full control over tiers, roles, and layout | Free | Communities that want their own branded board |
| tracklock.gg | Live data tracker | Sort by win rate/pick rate only | Free | Checking current numbers fast |
| metabot.gg | Curated tier list | Read-only | Free | Quick reference without building anything |
| findingdulcinea.com | Curated tier list | Read-only | Free | Meta-impact breakdown by hero count |
| Generic tier-list template sites | Drag-and-drop templates | Layout only, no live data | Free | One-off casual tier lists for fun |
The Complete Working Project
Put together, your project folder should now contain four files: index.html with the board markup and export buttons, style.css with the tier colors and mobile query, heroes.json with the full 38-hero roster, and app.js with the drag-and-drop logic, local storage persistence, optional live-data fetch, and export functions from Steps 5 through 9. That’s the entire application, no server-side code, no database, and no build step. If you followed each step in order, opening index.html in a browser (or visiting your deployed GitHub Pages URL) should give you a fully working Deadlock tier list maker you can rank, save, export, and share.
Total size of the project, before you add any hero art or extra libraries, comes in well under 20KB across the four files. That’s small enough to load instantly even on a mobile connection between matches, which matters more than it sounds since a slow-loading tier list defeats the point of having one you can check quickly during a draft. If you do want hero portraits instead of plain text cards, host them yourself rather than hotlinking from another site, since hotlinked images tend to break the moment the source site reorganizes its folders.
Frequently Asked Questions
Do I need to know JavaScript to follow this tutorial?
Basic comfort helps, but every code block in this guide is copy-paste ready. You mainly need to know how to save files and open a terminal.
How many heroes does Deadlock have as of August 2026?
38 heroes, based on Build 6675 tier list data published in mid-August 2026. Valve has added new heroes throughout 2026, including Celeste in early February, so expect the count to keep climbing.
Why build a maker instead of just using an existing tier list?
Existing trackers reflect someone else’s ranking logic and rank bracket. A maker you control lets you weigh win rate, personal experience, and your own team’s comp needs differently, and update it the moment you disagree with a patch’s impact.
Is it safe to pull data from third-party Deadlock stat sites?
Reading public JSON endpoints from community sites is generally fine, but always check their terms and current documentation first, since rate limits and schemas change. Wrap the fetch in a try/catch so your app degrades gracefully if a source goes offline.
Can I use this tier list maker for other games?
Yes. Swap out heroes.json for any roster (characters, weapons, or agents) and the drag-and-drop, local storage, and export logic all work unchanged. The tier row structure is game-agnostic by design.
How often should I update my tier list after a patch?
Valve has shipped meaningful balance changes roughly every two to four weeks in 2026. Give win rates 24-48 hours to stabilize after a patch before re-sorting, since early data is skewed by players testing changes rather than playing normally.
Does this work without any coding at all?
If you want zero code, a generic drag-and-drop tier list template site will get you a shareable image faster. This tutorial trades a bit of setup time for a tool you fully own, can rebrand, and can extend with features templates don’t offer, like live data or role filters.
What’s the fastest way to deploy this for a Discord community?
GitHub Pages, covered in Step 11, is the quickest free option since it needs no server management. Once live, pin the URL in your Discord server so members can build and compare their own tier lists.
Should I use letter tiers (S, A, B) or a numeric score?
Letter tiers, the S+ through D system used throughout this guide, are what most of the Deadlock community already reads, so stick with them if you want your board to be instantly understood. A numeric score (1-100 per hero, for example) is more precise and easier to sort programmatically, which makes it worth adding as a secondary field in heroes.json even if letters remain the primary display.




