Create an Archery Game using HTML, CSS, and JavaScript


By Faraz -

Learn how to make an exciting archery game from scratch using HTML, CSS, and JavaScript. Perfect for beginners in game development.

Create an Archery Game with HTML, CSS, and JavaScript.jpg

Welcome to our comprehensive guide on creating an archery game using HTML, CSS, and JavaScript. Whether you're a novice developer looking to embark on your coding journey or an experienced programmer seeking to expand your skill set, this tutorial provides a step-by-step walkthrough of the entire game development process.

Archery games have always fascinated players with their precision and challenge. By the end of this tutorial, you'll have the skills and knowledge to craft your own captivating browser-based archery game that can be shared and enjoyed by friends and gamers worldwide.

This tutorial will cover everything you need to know, from setting up the fundamental HTML structure to implementing interactive game elements using JavaScript. You don't need to be an expert to get started; we'll break down each step into simple, easy-to-understand instructions, making this tutorial accessible to beginners while still offering valuable insights to more seasoned developers.

By the time you've completed this guide, you'll have a functioning archery game and a strong foundation in web development and game design principles. So, let's grab our virtual bows and arrows and begin this exciting journey into the world of archery game development. Are you ready to take your first shot? Let's get started!

Source Code

Step 1 (HTML Code):

To get started, we will first need to create a basic HTML file. In this file, we will include the main structure for our archery game.

After creating the files just paste the following codes into your file. Make sure to save your HTML document with a .html extension, so that it can be properly viewed in a web browser.

Let me break down the code and explain each part:

1. <!DOCTYPE html>: This declaration specifies the document type and version of HTML being used, which is HTML5 in this case.

2. <html lang="en">: This is the opening tag for the HTML document and includes the "lang" attribute to specify that the document is in English.

3. <head>: This section contains meta-information about the document, such as the title and character encoding.

  • <title>Archery Game</title>: This sets the title of the web page to "Archery Game," which appears in the browser's title bar or tab.
  • <meta charset="UTF-8" />: This meta tag defines the character encoding for the document, which is UTF-8, a standard for encoding text in various languages.
  • <meta name="viewport" content="width=device-width" />: This meta tag is used to make the web page responsive by adjusting its width to the device's screen width.
  • <link rel="stylesheet" href="styles.css" />: This line links an external CSS stylesheet named "styles.css" to apply styles to the HTML content.

4. <body>: This section contains the main content of the web page.

  • <svg id="game" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 400" overflow="visible">: This SVG (Scalable Vector Graphics) element defines the game area. It has an ID of "game," specifies a viewBox, and sets overflow to "visible."
  • <linearGradient id="ArcGradient">: This defines a linear gradient with the ID "ArcGradient," which can be used to fill shapes with gradient colors.
  • <path id="arc" ...>: This <path> element represents an arc in the game. It uses a gradient defined earlier ("ArcGradient") for its stroke color and specifies various attributes like stroke width and pointer events.
  • <defs>: This section contains definitions that can be reused within the SVG.
  • <g id="arrow">: This defines a group with the ID "arrow" that contains elements for drawing an arrow.
  • <g id="target">: This defines a group with the ID "target" that contains elements for drawing a target.
  • <g id="bow" ...>: This defines a group with the ID "bow" that contains elements related to drawing a bow.
  • <g class="arrow-angle">: This group uses a <use> element to reference the "arrow" group, and it is positioned at a specific coordinate (x=100, y=250).
  • <clipPath id="mask">: This defines a clipping path with the ID "mask" used for masking certain elements on the page.
  • <g class="arrows" ...>: This is an empty group that can be used for displaying arrows within the defined clipping path.
  • <g class="miss" ...>: This group contains paths that define the visual representation of a missed shot in the game.
  • <g class="bullseye" ...>: This group contains paths that define the visual representation of a bullseye in the game.
  • <g class="hit" ...>: This group contains paths that define the visual representation of a successful hit in the game.
  • <span>Draw back an arrow and launch it!</span>: This is a text element that provides instructions or information to the user.
  • <script>: These script tags include references to external JavaScript files ("TweenMax.min.js," "MorphSVGPlugin.min.js," and "script.js") used for adding interactivity and animations to the web page.

This is the basic structure of our archery game using HTML, and now we can move on to styling it using CSS.

Step 2 (CSS Code):

Once the basic HTML structure of the archery game is in place, the next step is to add styling to the game using CSS.

Next, we will create our CSS file. In this file, we will use some basic CSS rules to style our game. Let's break down each part:

1. body: This is a CSS rule that targets the <body> element of the HTML document.

  • background: #222;: This sets the background color of the entire web page to a dark gray color (#222).
  • margin: 20px;: This adds a 20-pixel margin to all sides of the <body> element, creating space between the content and the edges of the page.

2. svg: This CSS rule targets all <svg> elements on the page. It's used to style the SVG graphics in the web page.

  • width: 100%;: This makes the width of the SVG element 100% of its containing parent, ensuring it spans the entire width of the viewport.
  • height: 100%;: Similarly, this makes the height of the SVG element 100% of its containing parent, ensuring it spans the entire height of the viewport.
  • position: fixed;: This sets the position of the SVG element to "fixed," which means it is positioned relative to the viewport and will not scroll with the rest of the page.
  • top: 0;: This positions the top edge of the SVG element at the top of the viewport.
  • left: 0;: This positions the left edge of the SVG element at the left side of the viewport.

3. span: This CSS rule targets all <span> elements on the page. It's used to style text enclosed within <span> tags.

  • color: white;: This sets the text color to white, making the text appear as white on the dark gray background defined for the <body>.
  • font-family: sans-serif;: This specifies the font family for the text enclosed within <span> tags as a generic sans-serif font.
  • opacity: 0.3;: This sets the opacity of the text to 0.3, making it somewhat transparent. This gives the text a faded appearance.

This will give our archery game an upgraded presentation. Create a CSS file with the name of styles.css and paste the given codes into your CSS file. Remember that you must create a file with the .css extension.


Step 3 (JavaScript Code):

Finally, we need to create a function in JavaScript. Let's break down what this code does step by step:

1. The code starts by selecting an SVG element from the DOM (Document Object Model) using document.querySelector("svg"). This SVG element is assumed to contain the graphics for the game.

2. It creates an SVG point called cursor using svg.createSVGPoint(). This point will be used to track the mouse cursor's position within the SVG.

3. The code also selects an element with the class "arrows" and initializes a variable randomAngle to zero. This likely represents the arrows in the game and the initial angle of the bow.

4. It defines several objects:

  • target: Represents the center of the target.
  • lineSegment: Represents a line segment used for the target intersection.
  • pivot: Represents the rotation point of the bow.

5. The aim function is defined. This function takes an event object e (likely a mouse event) as an argument. It calculates and updates various animations and transforms for the bow and arrow based on the mouse position. This function appears to handle the aiming of the bow.

6. The draw function is defined. This function is called when the mouse is clicked (mousedown event). It initializes the arrow drawing process, sets up event listeners for mouse movement (mousemove) and mouse release (mouseup), and calls the aim function to start aiming the bow.

7. The loose function is defined. This function is called when the mouse button is released (mouseup event). It releases the arrow, removes event listeners for mouse movement and release, animates the bow's return to its original position, duplicates the arrow, and animates the arrow's flight along a path.

8. The hitTest function checks for collisions between the arrow and the target. It uses mathematical calculations to determine if the arrow hits the target or misses it. Depending on the result, it calls the showMessage function with a selector for the appropriate message (e.g., "hit," "bullseye," or "miss").

9. The onMiss function is called when the arrow misses the target. It displays a "miss" message using the showMessage function.

10. The showMessage function handles the animation of text messages. It uses TweenMax (likely from the GSAP animation library) to animate the appearance and disappearance of messages.

11. Two utility functions, getMouseSVG and getIntersection, are defined. getMouseSVG normalizes the mouse position within the SVG, and getIntersection calculates the intersection point between two line segments and checks if the point is on either segment.

Create a JavaScript file with the name script.js and paste the given codes into your JavaScript file and make sure it's linked properly to your HTML document so that the scripts are executed on the page. Remember, you’ve to create a file with .js extension.

var svg = document.querySelector("svg");
var cursor = svg.createSVGPoint();
var arrows = document.querySelector(".arrows");
var randomAngle = 0;

// center of target
var target = {
	x: 900,
	y: 249.5

// target intersection line segment
var lineSegment = {
	x1: 875,
	y1: 280,
	x2: 925,
	y2: 220

// bow rotation point
var pivot = {
	x: 100,
	y: 250
	clientX: 320,
	clientY: 300

// set up start drag event
window.addEventListener("mousedown", draw);

function draw(e) {
	// pull back arrow
	randomAngle = (Math.random() * Math.PI * 0.03) - 0.015;
	TweenMax.to(".arrow-angle use", 0.3, {
		opacity: 1
	window.addEventListener("mousemove", aim);
	window.addEventListener("mouseup", loose);

function aim(e) {
	// get mouse position in relation to svg position and scale
	var point = getMouseSVG(e);
	point.x = Math.min(point.x, pivot.x - 7);
	point.y = Math.max(point.y, pivot.y + 7);
	var dx = point.x - pivot.x;
	var dy = point.y - pivot.y;
	// Make it more difficult by adding random angle each time
	var angle = Math.atan2(dy, dx) + randomAngle;
	var bowAngle = angle - Math.PI;
	var distance = Math.min(Math.sqrt((dx * dx) + (dy * dy)), 50);
	var scale = Math.min(Math.max(distance / 30, 1), 2);
	TweenMax.to("#bow", 0.3, {
		scaleX: scale,
		rotation: bowAngle + "rad",
		transformOrigin: "right center"
	var arrowX = Math.min(pivot.x - ((1 / scale) * distance), 88);
	TweenMax.to(".arrow-angle", 0.3, {
		rotation: bowAngle + "rad",
		svgOrigin: "100 250"
	TweenMax.to(".arrow-angle use", 0.3, {
		x: -distance
	TweenMax.to("#bow polyline", 0.3, {
		attr: {
			points: "88,200 " + Math.min(pivot.x - ((1 / scale) * distance), 88) + ",250 88,300"

	var radius = distance * 9;
	var offset = {
		x: (Math.cos(bowAngle) * radius),
		y: (Math.sin(bowAngle) * radius)
	var arcWidth = offset.x * 3;

	TweenMax.to("#arc", 0.3, {
		attr: {
			d: "M100,250c" + offset.x + "," + offset.y + "," + (arcWidth - offset.x) + "," + (offset.y + 50) + "," + arcWidth + ",50"
			autoAlpha: distance/60


function loose() {
	// release arrow
	window.removeEventListener("mousemove", aim);
	window.removeEventListener("mouseup", loose);

	TweenMax.to("#bow", 0.4, {
		scaleX: 1,
		transformOrigin: "right center",
		ease: Elastic.easeOut
	TweenMax.to("#bow polyline", 0.4, {
		attr: {
			points: "88,200 88,250 88,300"
		ease: Elastic.easeOut
	// duplicate arrow
	var newArrow = document.createElementNS("http://www.w3.org/2000/svg", "use");
	newArrow.setAttributeNS('http://www.w3.org/1999/xlink', 'href', "#arrow");
	// animate arrow along path
	var path = MorphSVGPlugin.pathDataToBezier("#arc");
	TweenMax.to([newArrow], 0.5, {
		force3D: true,
		bezier: {
			type: "cubic",
			values: path,
			autoRotate: ["x", "y", "rotation"]
		onUpdate: hitTest,
		onUpdateParams: ["{self}"],
		onComplete: onMiss,
		ease: Linear.easeNone
	TweenMax.to("#arc", 0.3, {
		opacity: 0
	//hide previous arrow
	TweenMax.set(".arrow-angle use", {
		opacity: 0

function hitTest(tween) {
	// check for collisions with arrow and target
	var arrow = tween.target[0];
	var transform = arrow._gsTransform;
	var radians = transform.rotation * Math.PI / 180;
	var arrowSegment = {
		x1: transform.x,
		y1: transform.y,
		x2: (Math.cos(radians) * 60) + transform.x,
		y2: (Math.sin(radians) * 60) + transform.y

	var intersection = getIntersection(arrowSegment, lineSegment);
	if (intersection.segment1 && intersection.segment2) {
		var dx = intersection.x - target.x;
		var dy = intersection.y - target.y;
		var distance = Math.sqrt((dx * dx) + (dy * dy));
		var selector = ".hit";
		if (distance < 7) {
			selector = ".bullseye"


function onMiss() {
	// Damn!

function showMessage(selector) {
	// handle all text animations by providing selector
	TweenMax.set(selector, {
		autoAlpha: 1
	TweenMax.staggerFromTo(selector + " path", .5, {
		rotation: -5,
		scale: 0,
		transformOrigin: "center"
	}, {
		scale: 1,
		ease: Back.easeOut
	}, .05);
	TweenMax.staggerTo(selector + " path", .3, {
		delay: 2,
		rotation: 20,
		scale: 0,
		ease: Back.easeIn
	}, .03);

function getMouseSVG(e) {
	// normalize mouse position within svg coordinates
	cursor.x = e.clientX;
	cursor.y = e.clientY;
	return cursor.matrixTransform(svg.getScreenCTM().inverse());

function getIntersection(segment1, segment2) {
	// find intersection point of two line segments and whether or not the point is on either line segment
	var dx1 = segment1.x2 - segment1.x1;
	var dy1 = segment1.y2 - segment1.y1;
	var dx2 = segment2.x2 - segment2.x1;
	var dy2 = segment2.y2 - segment2.y1;
	var cx = segment1.x1 - segment2.x1;
	var cy = segment1.y1 - segment2.y1;
	var denominator = dy2 * dx1 - dx2 * dy1;
	if (denominator == 0) {
		return null;
	var ua = (dx2 * cy - dy2 * cx) / denominator;
	var ub = (dx1 * cy - dy1 * cx) / denominator;
	return {
		x: segment1.x1 + ua * dx1,
		y: segment1.y1 + ua * dy1,
		segment1: ua >= 0 && ua <= 1,
		segment2: ub >= 0 && ub <= 1

Final Output:

Create an Archery Game with HTML, CSS, and JavaScript.gif


Congratulations, you've reached the bullseye of our archery game development tutorial! We hope you've enjoyed this journey into the world of HTML, CSS, and JavaScript game development. You've come a long way, and now you possess the knowledge and skills to create your own captivating browser-based archery game.

We hope this tutorial has ignited your passion for game development and provided you with the confidence to create your own projects. Remember, practice makes perfect, so don't hesitate to start building your unique games and sharing them with the world.

Thank you for joining us on this adventure in archery game development. We can't wait to see the incredible games you'll create in the future. Happy coding, and may your arrows always hit the mark!

Code by: Elliot Geno

That’s a wrap!

I hope you enjoyed this post. Now, with these examples, you can create your own amazing page.

Did you like it? Let me know in the comments below 🔥 and you can support me by buying me a coffee.

And don’t forget to sign up to our email newsletter so you can get useful content like this sent right to your inbox!

Faraz 😊

End of the article

Subscribe to my Newsletter

Get the latest posts delivered right to your inbox

Related Post