October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsSlow PC?RecommendedPC slow today? Run a repair scan before it gets worseResolve common Windows issues and optimize system performance.Scan NowOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
VGSources
ASP.NET

Build a Browser Game in an Hour with Visual Studio: A Modern ASP.NET-Friendly Ping Tutorial

Build Ping, a Pong-style browser game, while separating the useful JavaScript fundamentals from obsolete Visual Studio and ASP.NET setup instructions.

By VGSources Team 8 min read
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Ping is a small, client-side Pong variant built with HTML, CSS and JavaScript. The original tutorial used Visual Studio 2013 and an empty ASP.NET Web Site as its project container, but it did not use C# or server-side game logic. You can recreate the exercise today with a static web project, a local development server and the same browser APIs.

This is a guided demonstration, not a guaranteed beginner completion time. An experienced web developer can assemble the core game in about an hour; debugging touch input, responsive layout and collision edge cases will take longer.

What you are building

Ping places a player paddle on the left and a scripted opponent on the right. The ball crosses the arena, can be caught and fired back at an angle, and awards a point when the opposing side misses. Keyboard input supports desktop play, while on-screen controls target phones and tablets.

The result is a single-page browser game. It is not multiplayer and is not server-authoritative. Visual Studio supplies editing, debugging and local execution; the ASP.NET project contributes a hosting shell rather than gameplay code. The original article appeared in MSDN Magazine in March 2015 and was republished by SitePoint on August 27, 2015: Microsoft’s archive and SitePoint’s version.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Why use browser technologies?

  • HTML, CSS and JavaScript skills transfer directly to the game.
  • A browser can run the same files on desktop, tablet and phone without a native installation.
  • A handful of moving objects does not require Unity, Unreal, WebGL or a game engine.
  • Developer tools make the DOM, styles and runtime state easy to inspect.

There are limits. Mobile viewport changes, orientation, touch gestures, accessibility and refresh-rate differences require deliberate testing. For many objects, particles or effects, canvas is usually a better renderer than repeatedly moving DOM nodes.

Choose a current setup

Historical project

The original instructions use Visual Studio 2013 Professional (the code was also updated with the 2013 Community edition), an ASP.NET Empty Web Site, jQuery 2.1.1, image assets and a “Press Start 2P” font. The old menu path was File → New → ASP.NET Empty Web Site, followed by an HTML page, style.css, ping.js and the assets.

Practical modern project

Current Visual Studio installations may not include that Web Site template, and ASP.NET is not technically required. Create a folder with this layout and serve it through a local web server rather than opening file:/// directly:

ping/
  index.html
  style.css
  ping.js
  assets/
    arena.png
    sprites.png
    paddle.png
    ball.png
    press-start-2p.woff2

A minimal ASP.NET Core project, a static-file development server, or another local server can host the same files. Use the editor you already know; do not treat a particular Visual Studio release as a requirement.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Create the arena markup

Give each game entity a named element so the first version is easy to inspect:

<div id="arena">
  <div id="score" aria-live="polite">
    <h1>
      <span id="playerScore">0</span>
      <span id="opponentScore">0</span>
    </h1>
  </div>
  <div id="player" aria-label="Player paddle"></div>
  <div id="opponent" aria-label="Opponent paddle"></div>
  <div id="ball" aria-label="Ball"></div>
  <div id="controls-left">
    <button id="up" type="button" aria-label="Move up">▲</button>
    <button id="down" type="button" aria-label="Move down">▼</button>
  </div>
  <div id="controls-right">
    <button id="left" type="button" aria-label="Aim left">◀</button>
    <button id="right" type="button" aria-label="Aim right">▶</button>
  </div>
</div>

The original used div elements for controls; buttons add keyboard focus and accessible names without changing the game model.

Style a responsive playfield

The tutorial uses a full-screen, relatively positioned arena and absolutely positioned entities:

html, body {
  margin: 0;
  width: 100%;
  height: 100%;
  overflow: hidden;
}

#arena {
  position: relative;
  width: 100%;
  height: 100dvh;
  min-height: 320px;
  overflow: hidden;
  background: url("assets/arena.png") center / cover no-repeat;
}

#score {
  position: absolute;
  z-index: 2;
  left: 50%;
  top: 5%;
  transform: translateX(-50%);
}

#player, #opponent, #ball { position: absolute; }

#controls-left, #controls-right {
  position: absolute;
  bottom: max(1rem, env(safe-area-inset-bottom));
  display: flex;
  gap: .5rem;
  touch-action: none;
  user-select: none;
}

100dvh tracks the visible mobile viewport more reliably than a plain percentage height; include a fallback if supporting older browsers. The original stretched its background to the arena and warned that portrait mode could look awkward. Test landscape and portrait separately, account for safe-area insets, and decide whether cover cropping or a fixed logical game size better suits your art.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Load sprites and fonts carefully

The historical design uses a sprite sheet: one image containing several control or game graphics, revealed with CSS background-position. This keeps a pixel-art style consistent. Modern HTTP/2 and compression reduce the request-saving advantage, but sprites remain practical for small retro assets.

Plan for an arena background, paddle and ball artwork, control icons, the sprite sheet and the font file. Keep them local or serve them over HTTPS. The old jQuery reference was:

<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.1.1.min.js"></script>
<script src="ping.js"></script>
<link rel="stylesheet" href="style.css">

jQuery 2.1.1 is a historical dependency, not a current recommendation. Either retain it explicitly for a line-by-line reproduction or use a clearly labeled vanilla-JavaScript rewrite. Do not mix assumptions from both versions.

Build a time-based animation loop

Use requestAnimationFrame and velocity in pixels per second, not a fixed movement per frame:

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
let previousTime = 0;

function update(time) {
  const deltaTime = Math.min((time - previousTime) / 1000, 0.05);
  previousTime = time;
  updateGame(deltaTime);
  render();
  requestAnimationFrame(update);
}

requestAnimationFrame(update);
  1. Wait for the document and required assets to be ready.
  2. Create ball, paddle and score state.
  3. Compute elapsed time in seconds and clamp it after a backgrounded tab resumes.
  4. Advance the simulation, resolve collisions and update scores.
  5. Render positions, then schedule the next frame.

Keeping simulation and rendering as separate functions makes later migration to canvas easier. In a larger DOM game, batch style writes and avoid unnecessary layout reads.

Move the ball and resolve collisions

The original represents position and velocity as two-element arrays and applies elapsed time:

position[0] += velocity[0] * elapsed;
position[1] += velocity[1] * elapsed;

Use bounding boxes for the ball and paddles. Reverse vertical velocity at the top and bottom limits, and clamp paddle positions so they cannot leave the arena. At a paddle hit, resolve penetration before reversing direction; otherwise the ball can remain inside the paddle and trigger repeated collisions. Only reverse when the ball is travelling toward the paddle.

When the ball crosses the left or right scoring boundary, increment the opposite score, reset positions and establish the next owner. A more responsive game changes the outgoing angle according to the impact point: a hit near the paddle’s centre produces a shallow angle, while a hit near an edge produces a steeper one. At high speeds, discrete frames can tunnel through a paddle; continuous collision checks or a smaller time step may be necessary.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Add keyboard and pointer controls

Keyboard controls are useful on desktop, while pointer controls replace separate mouse and touch code on mobile:

const keys = new Set();
window.addEventListener("keydown", event => keys.add(event.key));
window.addEventListener("keyup", event => keys.delete(event.key));

const control = document.querySelector("#up");
control.addEventListener("pointerdown", event => {
  event.preventDefault();
  control.setPointerCapture(event.pointerId);
  player.moveUp = true;
});
["pointerup", "pointercancel", "lostpointercapture"].forEach(type =>
  control.addEventListener(type, () => { player.moveUp = false; }));

Release movement on pointerup, pointercancel and lost capture so a finger leaving the button cannot leave the paddle moving forever. Set touch-action: none on the controls to prevent scrolling, keep visible focus styles, offer keyboard alternatives and provide a pause mechanism. Do not communicate state through color alone.

Model catching and firing as explicit state

Ping’s distinctive mechanic is ball ownership. A small state machine is clearer than unrelated boolean flags:

free → moving toward player
moving → paddle collision
collided → owned by player
owned → aimed or positioned
owned → fired → moving toward opponent

Decide and document whether a player can hold the ball indefinitely, whether the opponent uses the same capture rule and what reset follows a score. Those are game-design choices, not consequences of ASP.NET. Store the owner, aim and fire transition together so a collision cannot both capture and score in the same update.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Implement the opponent as a simple state machine

The original opponent is scripted behavior, not machine learning. Its useful states are:

Following

Move toward the ball with a maximum tracking speed. Limiting speed prevents an unbeatable paddle.

Aiming and shooting

Choose a direction, apply an angle or controlled error, then fire. Separate the decision from movement so difficulty can be tuned.

Waiting

Pause between actions with a reaction delay. Randomized timing makes the opponent less mechanical without pretending it is intelligent.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

For difficulty levels, vary reaction delay, tracking speed, prediction distance and aiming error. Test each level on both slow and high-refresh-rate displays.

Test the game instead of trusting the demo

  • Play with a desktop keyboard and verify focus and restart behavior.
  • Test touch controls in landscape and portrait, including pointer cancellation and page scrolling.
  • Resize from narrow phones to wide monitors and check safe-area placement.
  • Background the tab, return to it and confirm the clamped time step prevents a giant jump.
  • Try high-DPI and high-refresh-rate screens and a slower device.
  • Check reduced-motion preferences, readable score announcements and non-color feedback.
  • Use browser developer tools’ Console and Network panels to find syntax errors and missing assets.

Blank page

Check filenames, stylesheet syntax, script order, missing assets and console errors. A blocked insecure http resource on an https page can also stop loading.

Ball does not move

Confirm that requestAnimationFrame starts and schedules itself again, elapsed time is initialized, the ball has position: absolute, and no exception has stopped the loop.

Ball speed is wrong

Check the milliseconds-to-seconds conversion and clamp unusually large deltas:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const deltaTime = Math.min((time - previousTime) / 1000, 0.05);

Collision glitches

Inspect actual element dimensions, resolve penetration, and reject a second collision while the ball is travelling away. Consider continuous collision detection when velocity rises.

When to choose another architecture

Approach Best fit Trade-off
DOM and CSS A few entities and a teaching project Familiar and inspectable, but scales poorly as moving-object count grows
Canvas Many sprites, particles and effects Centralized 2D rendering, but you must implement text, scaling and accessibility
Static hosting A one-player prototype Simplest deployment; no server state
ASP.NET with server features Accounts, persistence, telemetry or future matchmaking Adds server architecture that Ping itself does not need

Multiplayer is a different project: it needs authoritative state, real-time transport, reconnection, synchronization, latency handling and anti-cheat rules. Adding ASP.NET alone does not provide those systems.

What this tutorial teaches—and what is dated

The durable lessons are entity-based game state, time-based animation, collision geometry, explicit state machines, input cleanup and testing across viewports. The dated parts are the Visual Studio 2013 Web Site workflow, jQuery 2.1.1 and broad claims that the result automatically works on every PC, phone and tablet. Treat the 2015 tutorial as a clear browser-game exercise, then modernize the host, dependencies, viewport handling and accessibility before shipping.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from Patch Notes

Recommended PC Tool
Recommended PC Tool
Crashes, No Sound, or Screen Glitches?Free driver scan
Windows Errors? Fix Them Before They SpreadFree repair scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.