WebForms Core (WFC) is a server-side UI manipulation technology that lets developers control HTML elements and handle browser events through commands generated on the server. In this tutorial, we will use WebForms Core with C# and CodeBehind to build a simple bubble popping game.
Live demo: Bubble Popping Game
The game demonstrates how to create HTML elements dynamically, attach events, update the score, run intervals, and detect when an element exceeds a specified width, all through WebForms Core commands.
WebForms Core is a technology developed by Elanat and is available in 18 programming languages.
Note: WebForms Core can be used to build simple to moderately complex games, including animated games and logic-based games such as Memory. For large or highly complex games, specialized technologies and tools, such as WebGL, WebGPU, and dedicated game engines, are recommended.
1. Defining the Game Interface
The page contains a game area, a score counter, a speed counter, a message for the end of the game, and a button to start a new game.
<div id="GameArea"></div>
<br />
<b id="Points">Points: <span>0</span></b>
<b id="Speed">Speed: <span>5</span></b>
<b id="Finall"></b>
<input id="PointsValue" type="hidden" value="0">
<input id="SpeedValue" type="hidden" value="5">
<input id="Start" type="button" value="Start New">
The GameArea element is the main container for the bubbles. The Points and Speed elements display the current game values, while the hidden inputs store those values so that WebForms Core commands can read and update them.
The Start button restarts the game, and Finall displays the player's final score when the game ends.
Styling the bubbles
CSS gives the game its visual appearance. The game area uses a light blue background, while each bubble is a green circle with white curved lines to create a three-dimensional appearance.
#GameArea {
width: 1280px;
height: 720px;
background-color: lightblue;
overflow-x: auto;
white-space: nowrap;
display: inline-block;
}
.bubblebox {
width: 100%;
height: 100px;
}
.bubble {
width: 100px;
height: 100px;
background-color: #c9e83b;
border-radius: 50%;
box-shadow: -10px 0px 10px rgba(0, 0, 0, 0.3);
float: left;
position: relative;
overflow: hidden;
}
.pusher {
width: 100px;
height: 100px;
float: left;
}
The .bubblebox class represents a row containing a pusher and a bubble. The .pusher element occupies space before the bubble, allowing the bubble's horizontal position to change as the pusher grows.
The remaining CSS uses ::before and ::after pseudo-elements to draw decorative curves inside each bubble. Attribute selectors such as [bubblerow="1"] assign different top margins to different rows.
2. Initializing WebForms Core
The main controller is BubblePoppingGameController, which inherits from CodeBehindController.
using CodeBehind;
public partial class BubblePoppingGameController
: CodeBehindController
{
public void PageLoad(HttpContext context)
{
WebForms form = new WebForms();
form.SetCommentEventListener(
"GameArea", "mutationobserver", "end");
form.SetCommentEvent(
"Start", HtmlEvent.OnClick, "start");
// Game commands are defined here.
}
}
The WebForms class is used to define the commands that control the game.
Two event configurations are especially important:
-
SetCommentEventListenerregisters a MutationObserver-based listener for changes inGameArea. The listener invokes theendcommand index when the configured event condition is met. -
SetCommentEventassociates the button's click event with thestartcommand index.
The controller then defines the command sequences for starting, playing, and ending the game. WebForms Core exports these commands as HTML comments, which WebFormsJS processes in the browser.
This approach allows the developer to define UI operations and event handling in C# without manually writing JavaScript for each interaction.
3. Starting a New Game
The start command index initializes the game:
form.StartIndex("start");
form.AppendForm(Reset());
form.SetText("Finall", "");
form.SetValue("PointsValue", "0");
form.SetText(
"Points|<span>",
Fetch.GetValue("PointsValue"));
form.SetValue("SpeedValue", "5");
form.SetText(
"Speed|<span>",
Fetch.GetValue("SpeedValue"));
form.AppendForm(Initialization());
form.IncreaseWidth("{pusher}-1", 5);
form.AssignInterval(100, "pusher");
Each operation has a specific purpose.
First, Reset() removes the existing game elements and stops the intervals from the previous game. The final-score message is cleared, and the score and speed values are restored to their initial values.
Fetch.GetValue() reads the value of a hidden input. The result is then used by SetText() to update the corresponding display.
Next, Initialization() creates the first row and its bubble.
Finally, IncreaseWidth() increases the width of the pusher, and AssignInterval() schedules repeated execution of the command associated with the pusher index.
This is an important characteristic of WebForms Core: an interval can repeatedly execute a sequence of UI commands without requiring a custom JavaScript timer for the game logic.
4. Creating Bubbles Dynamically
The Initialization() method defines the structure of a bubble row.
private WebForms Initialization()
{
WebForms form = new WebForms();
form.AddTag("GameArea", "div");
form.SetClass(
"GameArea|<div>-1", "bubblebox");
form.SetAttribute(
"{bubblebox}-1",
"bubblerow",
Fetch.Random(1, 8));
form.AddTag("{bubblebox}-1", "div");
form.SetClass(
"{bubblebox}-1|<div>-1", "pusher");
form.AddTag("{bubblebox}-1", "div");
form.SetClass(
"{bubblebox}-1|<div>-1", "bubble");
form.SetCommentEvent(
"{bubble}-1",
HtmlEvent.OnMouseEnter,
"popping");
// Movement and overflow detection follow.
return form;
}
The method creates three elements:
- A
bubbleboxcontainer insideGameArea. - A
pusherelement inside the container. - A
bubbleelement beside the pusher.
SetClass() assigns the CSS classes, while SetAttribute() adds a bubblerow attribute. Fetch.Random(1, 8) supplies the row number used to determine the bubble's vertical placement.
The last command associates the bubble's mouseover-style event, HtmlEvent.OnMouseEnter, with the popping command index.
When the pointer enters a bubble, WebForms Core can therefore execute the commands defined under popping.
The important point is that the bubble elements are created dynamically. They do not need to be written individually into the original HTML page.
5. Moving the Bubble
The pusher controls the bubble's horizontal position. Its width increases over time, making the bubble move across the game area.
form.IncreaseWidth("{pusher}-1", 1);
form.AssignInterval(10, "pusher-pluse");
The pusher-pluse command index increases the pusher's width by one unit. The interval is assigned a delay of 10 milliseconds.
When the game starts, the pusher index is also assigned a 100-millisecond interval:
form.IncreaseWidth("{pusher}-1", 5);
form.AssignInterval(100, "pusher");
These two intervals perform different roles: pusher-pluse controls the continuous growth of the pusher, while pusher is the separate interval configured during game initialization.
The game also needs to detect when the pusher grows too large. WebForms Core provides a custom DOM event for this purpose.
form.CreateCustomDOMEvent(
"{pusher}-1",
"widthOverflow",
"style",
"width",
"greater",
"1100",
"",
true,
200);
form.SetCommentEventListener(
"{pusher}-1",
"widthOverflow",
"end");
The custom event monitors the pusher's width style and checks whether it exceeds 1100. Its configuration also includes the event options true and 200.
When widthOverflow occurs, the listener invokes the end command index. This connects the movement logic to the end-of-game logic without requiring a manually written JavaScript event handler.
6. Popping a Bubble and Updating the Score
The popping command index defines what happens when the player touches a bubble.
form.StartIndex("popping");
form.IncreaseValue("PointsValue", 1);
form.SetText(
"Points|<span>",
Fetch.GetValue("PointsValue"));
form.IncreaseValue("SpeedValue", 1);
form.SetText(
"Speed|<span>",
Fetch.GetValue("SpeedValue"));
form.Delete("$/");
form.AppendForm(Initialization());
First, IncreaseValue() increments the score stored in PointsValue. Fetch.GetValue() retrieves the updated value, and SetText() refreshes the score shown on the page.
The speed value is updated in the same way. Each successful bubble interaction increases it by one and refreshes the display.
The command Delete("$/") removes the element selected by that input-place expression. The game then calls Initialization() again, creating another bubble row.
Consequently, a successful interaction updates the counters, removes the targeted element, and creates another bubble. The player can continue interacting with bubbles until the overflow event ends the game.
In this implementation, the speed counter is incremented as a displayed value, but the interval delays themselves remain fixed at 100 and 10 milliseconds.
7. Ending the Game
When the overflow listener invokes the end command index, the controller displays the final score and resets the game elements.
form.StartIndex("end");
form.SetText(
"Finall",
"@:Your point is: " +
form.Inject(
Fetch.GetText("Points|<span>")));
form.AppendForm(Reset());
Fetch.GetText() reads the displayed score, and Inject() inserts the retrieved text into the command being constructed.
SetText() then displays the final-score message inside Finall. The Reset() method removes the game elements and stops the intervals.
The reset method is defined as follows:
private WebForms Reset()
{
WebForms form = new WebForms();
form.ElementExists("GameArea|<div>");
form.Delete("GameArea|<div>");
form.DeleteInterval("pusher");
form.DeleteInterval("pusher-pluse");
return form;
}
The element-existence check precedes the deletion of the game rows. The method also removes both named intervals, preventing the previous game's movement commands from continuing after a restart or game end.
The Start New button can then execute the start command index to initialize a fresh game.
8. Exporting the Commands
At the end of PageLoad(), the controller exports the commands:
Write(form.ExportToHtmlComment());
This writes the WebForms Core command representation as HTML comments in the response.
WebFormsJS processes these comments in the browser and executes the corresponding operations against the actual HTML DOM. The server defines the command sequences, while the client executes them.
The result is a game whose interface is built and manipulated through WebForms Core rather than through a collection of hand-written JavaScript DOM operations.
Screenshot of Bubble Popping Game
Conclusion
The Bubble Popping Game demonstrates several practical WebForms Core capabilities in a single example:
- Dynamically creating HTML elements and assigning CSS classes and attributes.
- Connecting browser events to named command indexes.
- Updating values and displayed text.
- Executing repeated commands through intervals.
- Detecting DOM conditions through custom events.
- Removing elements, stopping intervals, and restarting the game.
These features make it possible to implement interactive browser experiences using C# and WebForms Core, with WebFormsJS executing the generated commands in the browser.
Try the game: Open the Bubble Popping Game demo


Top comments (0)