{"id":13553302,"url":"https://github.com/eperezcosano/hexagonal-grid","last_synced_at":"2025-10-27T10:01:21.221Z","repository":{"id":119868207,"uuid":"281974345","full_name":"eperezcosano/hexagonal-grid","owner":"eperezcosano","description":"How to get a perfect hexagon grid using JavaScript to draw on a HTML canvas.","archived":false,"fork":false,"pushed_at":"2022-11-22T09:04:30.000Z","size":27,"stargazers_count":23,"open_issues_count":1,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-11-04T00:33:09.647Z","etag":null,"topics":["canvas","hexagon","hexagonal-grids","javascript","polygons"],"latest_commit_sha":null,"homepage":"https://eperezcosano.github.io/hex-grid/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/eperezcosano.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2020-07-23T14:29:29.000Z","updated_at":"2024-06-20T17:45:54.000Z","dependencies_parsed_at":"2023-06-03T11:15:36.389Z","dependency_job_id":null,"html_url":"https://github.com/eperezcosano/hexagonal-grid","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eperezcosano%2Fhexagonal-grid","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eperezcosano%2Fhexagonal-grid/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eperezcosano%2Fhexagonal-grid/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eperezcosano%2Fhexagonal-grid/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/eperezcosano","download_url":"https://codeload.github.com/eperezcosano/hexagonal-grid/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246939195,"owners_count":20857916,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["canvas","hexagon","hexagonal-grids","javascript","polygons"],"created_at":"2024-08-01T12:02:21.756Z","updated_at":"2025-10-27T10:01:21.210Z","avatar_url":"https://github.com/eperezcosano.png","language":"JavaScript","funding_links":[],"categories":["JavaScript","javascript"],"sub_categories":[],"readme":"# How to draw a hexagonal grid on HTML Canvas\n\nIn this article we are going to learn how to get a perfect hexagon grid using JavaScript to draw on a HTML canvas. We first need to know a bit of trigonometry to solve this problem as it is necessary for all the calculations for the coordinate points composing a regular polygon.\n\n## Table of Contents\n1. [The Basics](#the-basics)\n2. [A Hexagon](#a-hexagon)\n3. [A Row](#a-row)\n4. [The Grid](#the-grid)\n\n\n## The Basics\n\nFirst of all, we introduce a regular hexagon that is composed of six equal sides.\n\n\u003cdiv style=\"text-align:center\"\u003e\u003cimg src=\"https://eperezcosano.github.io/f6c3552346a2bee79f9d4f616143a811/hex.svg\" alt=\"hexagon\" width=\"150\"/\u003e\u003c/div\u003e\n\nAny regular polygon can be inscribed within a circumference of radius **r**\n\n\u003cdiv style=\"text-align:center\"\u003e\u003cimg src=\"https://eperezcosano.github.io/fb798448f4bb72e11b6f701141e6cf44/circumference.svg\" alt=\"circumference\" width=\"250\"/\u003e\u003c/div\u003e\n\nSo each of its vertexes intersects with the circumference. Drawing from the premise that the center of the circumference is the point of origin _(0,0)_ we can easily calculate the most-right and most-left vertex are _(**r**,\u0026nbsp;0)_ and _(-**r**,0)_ respectively, however, what are the positions of the rest of the points? Here is where trigonometry comes into play.\n\nGiven any right triangle, the following trigonometric functions applies:\n\n\u003cdiv style=\"text-align:center\"\u003e\u003cimg src=\"https://eperezcosano.github.io/e1a78d00b463587772f7a422cd9cc181/trigo.svg\" alt=\"trigonometry\" width=\"250\"/\u003e\u003c/div\u003e\n\nIt is very useful to know any side of the triangle if you know one of its other sides and the angle it forms. For this case, the angle formed by each vertex with the horizontal axis is equal by dividing the circumference by the number of sides (360º\u0026nbsp;/\u0026nbsp;6\u0026nbsp;=\u0026nbsp;**60º**) and we also know that the hypotenuse is equal to the radius of the circumference **r**. From the first equation we can say that a\u0026nbsp;=\u0026nbsp;c\u0026nbsp;*\u0026nbsp;sinα and b\u0026nbsp;=\u0026nbsp;c\u0026nbsp;*cosα. In summary, putting altogether the second vertex coordinates are _(**rcos60º**,**rsin60º**)_.\n\n![](https://eperezcosano.github.io/e54179cfce3b7e01ada66b0ee7d3beb4/trigo2.svg)\n\nThen the rest comes as a multiple of 60º as 120º, 180º, 240º, 300º and 360º which is equal to 0º again. Notice that the most-right and most-left vertex coincide with what we have expected due to sin0º = 0, cos0º = 1, cos180º = -1 and sin0º = 0. These are the resulting vertexes:\n\n![](https://eperezcosano.github.io/8124cf09214dbaf0e8a7b3bacaab141b/trigo3.svg)\n\n## A Hexagon\n\nAs this point we can start a new project to put in practice all we have seen. In an **index.html** file we set the minimum required fields for a HTML canvas:\n\n```html\n\u003c!DOCTYPE HTML\u003e\n\u003chtml lang=\"en\"\u003e\n  \u003chead\u003e\n  \t\u003cmeta charset=\"UTF-8\"\u003e\n  \t\u003ctitle\u003eHexGrid\u003c/title\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    \u003ccanvas id=\"canvas\" width=\"800\" height=\"500\"/\u003e\n    \u003cscript src=\"main.js\"\u003e\u003c/script\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\nAnd a **main.js** file:\n```javascript\nconst canvas = document.getElementById('canvas');\nconst ctx = canvas.getContext('2d');\n\nfunction init() {}\ninit();\n```\nAs far as we know, we are going to set up the angle and the size of the hexagon as constants. Notice the angles are needed to be expressed in radians (360º\u0026nbsp;=\u0026nbsp;2π\u0026nbsp;rad)\n```javascript\nconst a = 2 * Math.PI / 6;\nconst r = 50;\n```\nIn order to draw a regular hexagon we define a function named _drawHexagon(x,y)_ being _x_ and _y_ the center point. We are going to use a path that allows to set the coordinates before drawing them and when finished we use _stroke()_ to draw only the border line. It is possible doing a _for loop_ to draw a line between each vertex so the result is as follows:\n```javascript\nfunction drawHexagon(x, y) {\n  ctx.beginPath();\n  for (var i = 0; i \u003c 6; i++) {\n    ctx.lineTo(x + r * Math.cos(a * i), y + r * Math.sin(a * i));\n  }\n  ctx.closePath();\n  ctx.stroke();\n}\n```\nBefore testing it, notice that the point _(0,0)_ in our canvas starts on the upper left corner, so in order to fit the drawing we need a minimum offset of **r**.\n![](https://eperezcosano.github.io/e43e70506381d06f0d758f5d1ed8fd03/borders.svg)\n\n### Result\nhttps://codepen.io/eperezcosano/pen/eYJXzXK\n\n## A Row\n\nPerfect! The next step is to draw a row of hexagons, like that:\n\n![](https://eperezcosano.github.io/7edc8cca90ccd1442481751622ce7a78/row.svg)\n\nEssentially it is important to know where the next center is going to be located to fit perfectly with one another. First, notice how much horizontally is placed the purple arrow. It is a distance of the radius **r** plus a segment we already know as **rcos60º**. And same as vertically, a segment of **rsin60º** downwards. The procedure is always adding the same amount horizontally and alternating vertically.\n\nThe code that allows to draw the four hexagons showed before is:\n\n```javascript\n// 1st\nx = r;\ny = r;\ndrawHexagon(x, y);\n\n// 2nd\nx = x + r + r * Math.cos(a);\ny = y + r * Math.sin(a);\ndrawHexagon(x, y);\n\n// 3rd\nx = x + r + r * Math.cos(a);\ny = y - r * Math.sin(a);\ndrawHexagon(x, y);\n\n// 4th\nx = x + r + r * Math.cos(a);\ny = y + r * Math.sin(a);\ndrawHexagon(x, y);\n```\n\n### Result\nhttps://codepen.io/eperezcosano/pen/xxZBEwN\n\nWe need to find the pattern that will allow to made this scalable.\nOn the one hand, _x_ could be written as a increment of:\n```javascript\nx = x + r + r * Math.cos(a);\n```\nThat shortened is expressed as:\n```javascript\n  x += r * (1 + Math.cos(a));\n```\nOn the other hand, _y_ is altered between adding or subtracting whether it is an even or odd position:\n```javascript\ny = y + r * Math.sin(a); // Even position\ny = y - r * Math.sin(a); // Odd position\n```\nHow it could be written for a general case?\nLet's assign a new variable _j_ that increases just as it does the position we are in. If we use this mathematical trick, we can do like an if-statement for alternating whether is an even or an odd number:\n```\n(-1) ** j = -1 when j is odd\n(-1) ** j = 1 when j is even\n```\nThat is exactly what we were looking for! Let's wrap in altogether, and _y_ is expressed for every iteration as:\n```javascript\nj++;\ny = y + (-1) ** j * r * Math.sin(a);\n```\nThat shortened is expressed as:\n```javascript\ny += (-1) ** j++ * r * Math.sin(a);\n```\nFinally we arrive to the solution on how to draw many hexagons in a row as we initially intended. We define a function named _drawGrid(width,height)_ that prints what we have just explained up to this point:\n```javascript\nfunction drawGrid(width, height) {\n  let y = r;\n  for (let x = r, j = 0; x + r * (1 + Math.cos(a)) \u003c width; x += r * (1 + Math.cos(a)), y += (-1) ** j++ * r * Math.sin(a)) {\n    drawHexagon(x, y);\n  }\n}\n```\nNotice whether the subsequent hexagon, in every iteration, that we are going to draw fits inside the canvas.\n\n### Result\nhttps://codepen.io/eperezcosano/pen/XWXGKwP\n\n## The Grid\n\nThat is it! We are just one step away from success. All we need is to repeat the same procedure but in the row below repeatedly. But, how much lower is it from the original row? Let's find it out:\n\n![](https://eperezcosano.github.io/c92173d5dbbc6b8061598818d253339e/column.svg)\n\nThis would be the final scheme of our grid, showing the first four centers of each row to get a good view on what is going on. From the center _(0,0)_ we can see that the blue arrow takes a distance of twice the length of the hexagon height that sums up to **2rsin60º**. However, depending how many hexagons can fit in a row, we have to add **rsin60º** if is odd or **2rsin60º** if is even. The rest is going to be the same taking into account this offset. We modify our function to draw many lines as the last hexagon fits in the canvas height.\n\n```javascript\nfunction drawGrid(width, height) {\n  for (let y = r, j = 0; y + r * Math.sin(a) \u003c height; y += 2 ** ((j + 1) % 2) * r * Math.sin(a), j = 0) {\n    for (let x = r; x + r * (1 + Math.cos(a)) \u003c width; x += r * (1 + Math.cos(a)), y += (-1) ** j++ * r * Math.sin(a)) {\n      drawHexagon(x, y);\n    }\n  }\n}\n```\nLet's put altogether and try it out!\n\n### Result\nhttps://codepen.io/eperezcosano/pen/vYLPXYO\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feperezcosano%2Fhexagonal-grid","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feperezcosano%2Fhexagonal-grid","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feperezcosano%2Fhexagonal-grid/lists"}