S
Sam
Guest
What looked like eleven circles on a pitch turned into a surprisingly useful lesson in responsive design, positioning, touch interactions, and keeping a small web tool simple.
A football formation looks simple when you draw it on paper. Put a goalkeeper at the back, arrange the defenders, add a midfield, place a few attackers higher up the pitch, and you already have something that most football fans can recognize immediately.
That was roughly how I approached the idea when I started building a football formation tool. I wasn't trying to create a football management game or a complex tactical analysis platform. I wanted something much simpler: choose a formation, put players on a pitch, move them around, and create a lineup without having to install anything.
The first version came together quickly. Drawing the pitch itself was straightforward. A rectangular container, a halfway line, a centre circle, penalty areas and a few player markers were enough to make the page look like a football tool.
The problems began when I started using it rather than just looking at it.
A formation that looked fine on my screen didn't necessarily look right when the browser became narrower. Player positions that seemed natural on desktop became cramped on mobile. Moving players with a mouse felt easy, while doing the same thing with a finger introduced a completely different set of problems.
What I thought would mostly be a visual project gradually became an exercise in front-end design.
The pitch wasn't really the difficult part
Drawing a football pitch in the browser is not particularly complicated. CSS borders and absolutely positioned elements can handle most of the visual structure without needing a canvas or graphics library.
The harder question is where everything inside that pitch belongs.
Football positions are spatial. A player labelled
LB doesn't automatically look like a left-back. The marker needs to appear far enough to the left, sit at roughly the right depth, and still have enough room around it for the rest of the defensive line.The same applies everywhere else on the pitch. A winger needs width. A striker needs to look like a striker rather than another midfielder. Centre-backs should feel connected without sitting on top of each other.
That meant I couldn't think about the players as ordinary interface elements arranged in rows and columns. The football pitch needed its own coordinate system.
Once I started treating the pitch that way, the rest of the design became easier to reason about.
Fixed pixel positions didn't survive resizing
My first instinct was to place players using fixed positions. In its simplest form, that looks something like this:
Code:
.player {
position: absolute;
left: 220px;
top: 340px;
}
There is nothing inherently wrong with that if the pitch never changes size. The problem is that a browser-based tool has to work across many screen widths.
A player positioned 220 pixels from the left might be in exactly the right place on a desktop pitch. Reduce the pitch width substantially and those same 220 pixels represent a completely different part of the field.
I could have added separate coordinates for different breakpoints, but that would mean maintaining several versions of every formation.
Using relative positions was much cleaner.
Code:
.player {
position: absolute;
left: 50%;
top: 55%;
transform: translate(-50%, -50%);
}
Instead of thinking of a midfielder as being a certain number of pixels from the edge, I could think of the player as being halfway across the pitch and a certain percentage down it.
The pitch could then resize while the basic shape of the formation remained intact.
It was a small technical decision, but it changed the structure of the project. From that point on, player positions were no longer tied to the physical dimensions of the screen.
The formations belonged in data
The next decision was how to store the actual formations.
Hard-coding players into the page would have been manageable for two or three formations, but it would quickly become irritating to maintain. The interface itself was not changing when someone selected a 4-4-2 instead of a 4-3-3. Only the positions were changing.
So I separated the formation data from the pitch.
A simplified 4-3-3 can be represented like this:
Code:
const formation433 = [
{ role: "GK", x: 50, y: 90 },
{ role: "LB", x: 18, y: 72 },
{ role: "CB", x: 40, y: 75 },
{ role: "CB", x: 60, y: 75 },
{ role: "RB", x: 82, y: 72 },
{ role: "CM", x: 30, y: 50 },
{ role: "CM", x: 50, y: 56 },
{ role: "CM", x: 70, y: 50 },
{ role: "LW", x: 20, y: 25 },
{ role: "ST", x: 50, y: 18 },
{ role: "RW", x: 80, y: 25 }
];
Those values aren't supposed to represent real distances on a football field. They simply describe positions relative to the browser pitch.
Once the rendering logic understands that structure, other formations become much easier to add. A 4-4-2, 3-5-2 or 4-2-3-1 is another set of coordinates rather than another layout implementation.
This also kept the presentation separate from the football logic. The pitch was responsible for displaying players. The formation data was responsible for deciding where they started.
That separation made the code considerably easier to work with.
Football formations aren't as precise as they look
There was another issue that had nothing to do with JavaScript.
A formation name doesn't tell you exactly where every player should stand.
Take a 4-3-3. One team might use a defensive midfielder behind two more advanced players. Another might play the midfield almost flat. A third might stagger all three midfielders at slightly different heights.
All of them can still reasonably be described as a 4-3-3.
The same problem appears with other formations. In a 4-2-3-1, the wide attacking players may behave almost like wingers in one system and narrow attacking midfielders in another. A 3-5-2 can look very different depending on the position of the wing-backs.
Initially I spent too much time trying to decide on the "correct" location for individual positions.
Eventually I stopped treating the predefined formations as final answers.
They are starting points.
A formation builder becomes more useful when it gives someone a recognizable shape and then allows them to adjust it. That is much closer to how people actually think about football tactics anyway.
Mouse and touch interactions are different problems
Moving players around with a mouse worked reasonably well early in development. A mouse pointer is precise, and grabbing a small circular marker isn't particularly difficult.
Using the same interface on a phone exposed the weaknesses quickly.
A marker that looks perfectly large enough on a laptop can be frustrating to grab with a finger. Increasing the marker size helps interaction, but making every player much larger also makes the pitch feel crowded.
Touch also introduces scrolling. If a user tries to move a player vertically, the browser may interpret the gesture as an attempt to move the page rather than the player.
That meant the touch behaviour had to be considered separately rather than assuming the desktop interaction would simply carry over.
It also reinforced something that comes up regularly in front-end work: the visible size of an interface element and the area available for interacting with it don't necessarily have to be identical.
A player marker can remain visually compact while still having a more forgiving touch target.
These are small details, but they have a disproportionate effect on whether the tool feels pleasant to use.
Responsive design wasn't just about making the pitch smaller
The football pitch itself scales relatively well once the positions are based on percentages.
The rest of the interface required more thought.
On a large screen there is enough room for formation controls, player options and actions to sit comfortably around the pitch. On a phone, all of those controls have to share a much narrower space.
Simply reducing the size of everything didn't work particularly well.
Buttons sometimes needed to wrap. Controls needed more vertical breathing room. Player labels had to remain readable. The pitch itself still needed enough height to preserve a visible distinction between defence, midfield and attack.
Too little height and the formation begins to collapse visually. Too much height and the user has to scroll repeatedly to interact with the whole pitch.
There wasn't one technical solution that solved this. A surprising amount of the work was simply testing the interface at different widths and looking for places where it stopped feeling comfortable.
That kind of testing isn't sophisticated, but it matters.
A layout can be technically responsive and still be unpleasant to use.
I didn't want to add a framework unless the tool needed one
The finished project became the Football Formation Builder on Toolsyte.
For this particular tool, I kept the front-end relatively simple.
The application has a small set of responsibilities: render the pitch, load a formation, display players, respond to interactions and keep the interface usable at different screen sizes.
Plain HTML, CSS and JavaScript handle that perfectly well.
That isn't an argument that frameworks are unnecessary. For a larger application with more state, shared components and complicated data flows, a framework can make development considerably easier.
I just didn't want to introduce one because it felt like the expected thing to do.
There was also a practical benefit to keeping the implementation simple. When a player appeared in the wrong place, I could inspect the coordinates directly. When something broke at a particular screen width, the relevant CSS was easy to find. There weren't many layers between the behaviour I was seeing and the code responsible for it.
For small browser tools, that simplicity is valuable.
The difficult part was deciding what not to build
Once a basic tool works, adding features becomes tempting.
You can imagine player photos, custom colours, substitutes, tactical arrows, team logos, multiple pitch styles, saved formations, exports and dozens of other additions.
Some of those features may eventually be useful.
But every extra control makes the interface slightly more complicated.
The original reason for building the tool was simple: someone should be able to open the page, choose a formation and start arranging a team without having to learn how the software works.
That became a useful filter.
If a feature made that basic task noticeably harder, it probably didn't deserve to be added yet.
I've found this to be one of the more difficult parts of building small tools. Writing another function is often easier than deciding that another function isn't necessary.
Small tools expose design mistakes very quickly
I expected the football formation builder to be mostly a CSS exercise. It ended up touching positioning, responsive design, data modelling, pointer interactions and product decisions.
None of those individual problems was particularly unusual.
What made the project interesting was having all of them inside such a small interface.
A person using the tool doesn't care how the positions are stored. They don't care whether the interface uses fixed coordinates, percentages or some clever rendering system.
They care whether the formation looks right.
They care whether moving a player feels natural.
They care whether it still works when they open it on their phone.
That is one of the things I like about small web tools. They make it difficult to hide behind technical complexity. The result either feels simple or it doesn't.
The code behind a useful tool may involve a surprising number of decisions, but the person using it shouldn't have to think about any of them.
From their side of the screen, it should still feel like eleven players on a football pitch.
And if it does, most of the difficult work has done its job.