A interactive app for learning binary numbers through visual feedback and sound.
Code Walkthrough | Binary Basics
- Interactive Bit Toggling: Click any of the 8 bit boxes to toggle between 0 and 1. Each change updates the binary string and decimal value instantly.
- Keyboard Controls: Use ↑ and ↓ keys to increment or decrement the binary value. Smooth transitions with auditory feedback.
- Visual Breakdown: Displays the decimal value and its binary decomposition (e.g.,
32 + 8 + 2 = 42) for deeper understanding. - Place Value Labels: Each bit box is annotated with its corresponding power-of-two value (e.g., 128, 64, ..., 1).
- Responsive Layout: Automatically scales fonts and layout for HD/UHD resolutions and window resizing.
- Ambient Sound Loop: Background "ComputerPulsation" audio loops continuously to create an immersive experience.
- Overlapping Sound Effects: Instant playback of "CashCollected" sound on interaction, with overlapping enabled for responsiveness.
This app is ideal for:
- Teaching binary concepts in a visually engaging way
- Reinforcing place value and bit significance
- Creating emotionally affirming learning environments with sound and color
- Demonstrating how binary maps to decimal in real time
- VB.NET WinForms
- System.Drawing for custom rendering
- Custom AudioPlayer for sound layering and looping
- Dynamic font scaling for accessibility
- Clone the repo
- Build and run the project in Visual Studio
- Click or use arrow keys to explore binary values
- Enjoy the sound-enhanced feedback loop!
Binary is a numbering system using only two digits 0 and 1.
Each binary digit is called a bit.
Each bit has a place value, just like decimal — but based on powers of 2.
A group of 8 bits is called a byte.
With 8 bits, we can represent numbers from 0 to 255.
Imports System.ComponentModel
Imports System.IO
Imports System.Runtime.InteropServices
Imports System.Text- These bring in useful .NET libraries for file handling, UI components, and text encoding.
Public Class Form1- This is the main window of your app. Everything visual and interactive happens here.
Private BinaryStr As String
Private Bits(7) As Boolean
Private BitRects(7) As RectangleBinaryStr: Holds the current binary string (e.g.,"01010101").Bits: Boolean array representing each bit (True = 1, False = 0).BitRects: Rectangles for drawing clickable bit boxes.
Private BitBoxSize As Integer = 0
Private BitSpacing As Integer = 0
Private BitBoxesLeft As Integer = 0
Private BitBoxesTop As Integer = 0- These control the size and position of the bit boxes.
Private BitOnBrush = Brushes.Lime
Private BitOffBrush = Brushes.DarkGray
Private TextOnBrush = Brushes.Black
Private TextOffBrush = Brushes.Gray- Brushes define the colors for bits and text.
Private BitBoxFont As New Font("Consolas", 12)- Font used to draw the binary digits inside boxes.
Private DecimalVal As Integer
Private DecimalBrush = Brushes.White
Private DecimalFont = New Font("Consolas", 12)- Displays the decimal equivalent of the binary value.
Private PlaceValue As String
Private PlaceValueBrush = Brushes.LightGray
Private PlaceValueFont = New Font("Consolas", 12)- Shows the place value (128, 64, ..., 1) above each bit box.
Private ActiveValues As New List(Of Integer)
Private Breakdown As String
Private BreakdownBrush = Brushes.DarkGray
Private BreakdownFont = New Font("Consolas", 12)- Shows which place values are active and how they add up to the decimal value.
Private Player As AudioPlayer- Handles sound playback for feedback.
Private AlineCenter As New StringFormat With {.Alignment = StringAlignment.Center}
Private Graph As Graphics- Used to center text and draw graphics on the form.
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
InitializeApp()
End Sub- Initializes the app when the form loads.
Protected Overrides Sub OnPaint(e As PaintEventArgs)- Method Declaration: This line defines the
OnPaintmethod, which is protected and overrides the base class's method. It takes a parametereof typePaintEventArgs, which contains information about the paint event, including the graphics object used for drawing.
MyBase.OnPaint(e)- Call Base Method: This line calls the base class implementation of
OnPaint, ensuring that any default behavior defined in the parent class is executed before any custom painting occurs.
Graph = e.Graphics- Assign Graphics Object: This line assigns the
Graphicsobject from thePaintEventArgs(e.Graphics) to a class-level variableGraph. This allows for easy access to the graphics context for subsequent drawing operations.
Graph.CompositingMode = Drawing2D.CompositingMode.SourceOver- Set Compositing Mode: This line sets the compositing mode of the graphics object to
SourceOver. This mode ensures that the source (the drawn elements) is blended with the destination (the existing content) based on their alpha values, allowing for proper transparency handling.
Graph.TextRenderingHint = Drawing.Text.TextRenderingHint.AntiAlias- Set Text Rendering Hint: This line sets the text rendering hint to
AntiAlias, which improves the quality of rendered text by smoothing the edges. This results in a more visually appealing appearance for any text drawn on the control.
DrawBitBoxes()- Draw Bit Boxes: This line calls a method named
DrawBitBoxes(), which is responsible for rendering the graphical representation of the bit boxes. This method likely iterates through theBitsarray and draws each corresponding rectangle.
DrawDecimalValue()- Draw Decimal Value: This line calls a method named
DrawDecimalValue(), which is responsible for rendering the current decimal value represented by the bits. This could involve converting the binary representation to a decimal and displaying it on the control.
DrawActiveValuesBreakdown()- Draw Active Values Breakdown: This line calls a method named
DrawActiveValuesBreakdown(), which likely renders additional information about the active values or the state of the bits. This could include visual indicators or textual descriptions of the bits that are currently set toTrue.
End Sub- End Subroutine: This line indicates the end of the
OnPaintsubroutine.
The OnPaint subroutine is responsible for rendering the visual elements of the control. It initializes the graphics context, sets rendering properties for better visual quality, and calls specific methods to draw the bit boxes, the decimal value, and any additional information about the active values. This method is crucial for ensuring that the graphical representation of the data is updated and displayed correctly whenever the control needs to be repainted.
Protected Overrides Sub OnMouseClick(e As MouseEventArgs)- Method Declaration: This line defines the
OnMouseClickmethod, which is protected and overrides the base class's method. It takes a parametereof typeMouseEventArgs, which contains information about the mouse click event.
MyBase.OnMouseClick(e)- Call Base Method: This line calls the base class implementation of
OnMouseClick, ensuring that any default behavior defined in the parent class is executed.
For i = 0 To 7- Loop Initialization: This line starts a loop that will iterate from 0 to 7, allowing the code to check each of the 8 bit rectangles.
If BitRects(i).Contains(e.Location) Then- Check Click Location: This line checks if the location of the mouse click (
e.Location) is within the rectangle corresponding to the current bit (BitRects(i)). If it is, the following code will execute.
Player.PlayOverlapping("CashCollected")- Play Sound: This line plays a sound effect named "CashCollected" using a
Playerobject. This sound provides feedback to the user when they interact with a bit box.
Bits(i) = Not Bits(i)- Toggle Bit State: This line toggles the state of the bit at index
i. If the bit wasTrue(or1), it changes toFalse(or0), and vice versa. TheNotoperator inverts the boolean value.
Me.Invalidate()- Invalidate Control: This line calls
Invalidate()on the current control (Me), which marks the control for redrawing. This will trigger a repaint, updating the visual representation of the bit boxes to reflect the new state.
Exit For- Exit Loop: This line exits the
Forloop immediately after toggling the bit state for the clicked rectangle, preventing further checks since only one bit can be toggled per click.
Next- End Loop: This line marks the end of the
Forloop, which will continue to the next index until all bits are processed, but will not execute further if a bit has already been toggled.
End Sub- End Subroutine: This line indicates the end of the
OnMouseClicksubroutine.
The OnMouseClick subroutine allows users to interact with the graphical representation of bits by clicking on them. When a bit box is clicked, the corresponding bit's state is toggled, a sound is played for feedback, and the display is updated to reflect the change. This creates an interactive experience where users can manipulate binary values visually.
Protected Overrides Sub OnMouseMove(e As MouseEventArgs)- Method Declaration: This line defines the
OnMouseMovemethod, which is protected and overrides the base class's method. It takes a parametereof typeMouseEventArgs, which contains information about the mouse movement event.
MyBase.OnMouseMove(e)- Call Base Method: This line calls the base class implementation of
OnMouseMove, ensuring that any default behavior defined in the parent class is executed.
Dim newHoverIndex As Integer = -1- Initialize Hover Index: This line initializes a variable
newHoverIndexto -1. This variable will be used to track the index of the bit box currently being hovered over.
For i = 0 To 7- Loop Initialization: This line starts a loop that will iterate from 0 to 7, allowing the code to check each of the 8 bit rectangles.
If BitRects(i).Contains(e.Location) Then- Check Hover Location: This line checks if the location of the mouse (
e.Location) is within the rectangle corresponding to the current bit (BitRects(i)). If it is, the following code will execute.
newHoverIndex = i- Update Hover Index: If the mouse is over the current bit rectangle, this line updates
newHoverIndexto the current indexi.
Exit For- Exit Loop: This line exits the
Forloop immediately after finding the hovered bit box, preventing further checks since only one bit box can be hovered at a time.
Next- End Loop: This line marks the end of the
Forloop, which processes all 8 bit rectangles.
If newHoverIndex <> HoveredBitIndex Then- Check for Hover Change: This line checks if the newly detected hover index (
newHoverIndex) is different from the previously stored hover index (HoveredBitIndex).
HoveredBitIndex = newHoverIndex- Update Hovered Index: If the hover index has changed, this line updates
HoveredBitIndexto the new value.
Me.Invalidate()- Invalidate Control: This line calls
Invalidate()on the current control (Me), marking it for redrawing. This will trigger a repaint, updating the visual representation of the bit boxes to reflect the new hover state.
Cursor = If(HoveredBitIndex <> -1, Cursors.Hand, Cursors.Default)- Change Cursor: This line sets the cursor based on whether a bit box is being hovered over:
- If
HoveredBitIndexis not -1 (indicating a bit box is hovered), the cursor changes to a hand icon (Cursors.Hand). - If no bit box is hovered (i.e.,
HoveredBitIndexis -1), the cursor defaults to the standard arrow icon (Cursors.Default).
- If
End Sub- End Subroutine: This line indicates the end of the
OnMouseMovesubroutine.
The OnMouseMove subroutine updates the hover state of the bit boxes in response to mouse movement. It determines which bit box, if any, is currently being hovered over, updates the hover index, triggers a repaint of the control to reflect any changes, and adjusts the cursor to provide visual feedback to the user. This enhances the interactivity of the graphical user interface by indicating which bit box can be clicked.
Protected Overrides Sub OnKeyDown(e As KeyEventArgs)- Method Declaration: This line defines the
OnKeyDownmethod, which is protected and overrides the base class's method. It takes a parametereof typeKeyEventArgs, which contains information about the key event.
MyBase.OnKeyDown(e)- Call Base Method: This line calls the base class implementation of
OnKeyDown, ensuring that any default behavior defined in the parent class is executed.
Dim currentValue = Convert.ToInt32(String.Join("", Bits.Select(Function(b) If(b, "1", "0"))), 2)- Current Value Calculation: This line calculates the current integer value represented by the
Bitsarray:Bits.Select(Function(b) If(b, "1", "0"))converts each boolean bit to a string ("1" forTrue, "0" forFalse).String.Join("", ...)concatenates these strings into a single binary string.Convert.ToInt32(..., 2)converts the binary string to an integer (base 2).
If e.KeyCode = Keys.Up Then- Check for Up Arrow Key: This line checks if the up arrow key was pressed.
If currentValue < 255 Then- Upper Limit Check: This line checks if the current value is less than 255 (the maximum value for an 8-bit binary number).
currentValue += 1- Increment Value: If the current value is less than 255, this line increments it by 1.
Player.PlayOverlapping("CashCollected")- Play Sound: This line plays a sound effect named "CashCollected" to provide feedback to the user when the value is incremented.
End If- End If Statement: This line marks the end of the conditional check for the up arrow key.
ElseIf e.KeyCode = Keys.Down Then- Check for Down Arrow Key: This line checks if the down arrow key was pressed.
If currentValue > 0 Then- Lower Limit Check: This line checks if the current value is greater than 0.
currentValue -= 1- Decrement Value: If the current value is greater than 0, this line decrements it by 1.
Player.PlayOverlapping("CashCollected")- Play Sound: This line plays the same sound effect "CashCollected" to provide feedback to the user when the value is decremented.
End If- End If Statement: This line marks the end of the conditional check for the down arrow key.
Else- Else Condition: This line begins the else block, which handles cases where neither the up nor down key was pressed.
Return- Exit Method: This line exits the subroutine early if neither the up nor down key is pressed, preventing further execution.
End If- End If Statement: This line closes the outer if-else structure.
' Update bits array- Comment: This comment indicates that the following code will update the
Bitsarray based on the new value.
Dim binaryStr = Convert.ToString(currentValue, 2).PadLeft(8, "0"c)- Binary String Conversion: This line converts the
currentValueback to a binary string:Convert.ToString(currentValue, 2)converts the integer to a binary string..PadLeft(8, "0"c)ensures the string is 8 characters long, padding with leading zeros if necessary.
For i = 0 To 7- Loop Initialization: This line starts a loop that will iterate from 0 to 7, allowing the code to update each bit in the
Bitsarray.
Bits(i) = binaryStr(i) = "1"- Update Bits Array: This line updates the
Bitsarray:binaryStr(i) = "1"checks if thei-th character of the binary string is "1".- The result (a boolean value) is assigned to
Bits(i), setting the bit toTrueif the character is "1" andFalseotherwise.
Next- End Loop: This line marks the end of the
Forloop, which processes all 8 bits.
Me.Invalidate()- Invalidate Control: This line calls
Invalidate()on the current control (Me), marking it for redrawing. This will trigger a repaint, updating the visual representation of the bit boxes to reflect the new state.
End Sub- End Subroutine: This line indicates the end of the
OnKeyDownsubroutine.
The OnKeyDown subroutine allows users to increment or decrement a binary value using the up and down arrow keys. When the up arrow is pressed, the value increases (up to a maximum of 255), and when the down arrow is pressed, the value decreases (down to a minimum of 0). The corresponding sound is played for feedback, and the visual representation of the bits is updated to reflect the new value.
Private Sub Form1_Resize(sender As Object, e As EventArgs) Handles MyBase.Resize- Updates layout when the window size changes.
UpdateSizes()
UpdateStartPositions()
UpdateBitRects()
UpdateFonts()
Me.Invalidate()- Recalculates everything and redraws.
Private Sub Form1_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing
Player.CloseSounds()
End Sub- Cleans up audio resources.
Private Sub InitializeApp()- Sets up sound files, window properties, and bit box layout.
Private Sub UpdateBitRects()
Private Sub UpdateFonts()
Private Sub UpdateStartPositions()
Private Sub UpdateSizes()- Modular functions to calculate layout and font sizes based on window dimensions.
Private Sub DrawDecimalValue()- Converts bits to decimal and draws it centered above the boxes.
Private Sub DrawBitBoxes()- Purpose: This line defines a new subroutine named
DrawBitBoxes. ThePrivatekeyword indicates that this subroutine can only be accessed within the same class or module.
' Draw bit boxes.- Comment: This is a comment that describes what the following code will do. Comments are ignored by the compiler but are useful for developers to understand the code.
For i = 0 To 7- Loop Initialization: This line starts a loop that will iterate from 0 to 7, which means it will run 8 times (for each bit in a byte).
Rect = BitRects(i)- Rectangle Assignment: This line assigns the rectangle corresponding to the current bit (indexed by
i) from an array calledBitRects. Each rectangle represents the area where the bit will be drawn.
Graph.FillRectangle(If(Bits(i), BitOnBrush, BitOffBrush), Rect)- Fill Rectangle: This line fills the rectangle with a color based on the state of the bit (
Bits(i)). If the bit isTrue(or1), it usesBitOnBrush; ifFalse(or0), it usesBitOffBrush.
' Draw the binary digit centered horizontally and vertically inside of the bit boxes.- Comment: Another comment indicating that the next lines will handle drawing the binary digit.
Graph.DrawString(If(Bits(i), "1", "0"), BitBoxFont, If(Bits(i), TextOnBrush, TextOffBrush),
Rect.X + (BitBoxSize - Graph.MeasureString(If(Bits(i), "1", "0"), BitBoxFont).Width) / 2,
Rect.Y + (BitBoxSize - Graph.MeasureString(If(Bits(i), "1", "0"), BitBoxFont).Height) / 2)- Draw String: This complex line draws the binary digit (either "1" or "0") inside the rectangle:
- The first part
If(Bits(i), "1", "0")checks the bit value. BitBoxFontspecifies the font used for the text.If(Bits(i), TextOnBrush, TextOffBrush)chooses the text color based on the bit state.- The
XandYcoordinates are calculated to center the text within the box by subtracting half the width and height of the string from the rectangle's coordinates.
- The first part
' Draw place value above each bit box.- Comment: Indicates that the next lines will draw the place value for each bit.
PlaceValue = CStr(2 ^ (7 - i))- Place Value Calculation: This line calculates the place value of the bit. For a binary representation, the place value is determined as (2^{(7 - i)}). For example, when
iis 0, the place value is 128 (or (2^7)).
Graph.DrawString(PlaceValue, PlaceValueFont, PlaceValueBrush,
Rect.X + BitBoxSize \ 2,
Rect.Y - Me.ClientSize.Height \ 12,
AlineCenter)- Draw Place Value: This line draws the calculated place value above the corresponding bit box:
PlaceValueFontspecifies the font for the place value text.PlaceValueBrushis the color used for the text.- The coordinates position the place value above the box, adjusted by half the box size and a fraction of the client area height.
' Draw border if hovered.- Comment: Indicates that the next lines will check if the bit box is hovered over and draw a border if it is.
If i = HoveredBitIndex Then- Hover Check: This line checks if the current index
imatches the index of the hovered bit.
Using borderPen As New Pen(If(Bits(i), Color.DeepPink, Color.OrangeRed), BitSpacing / 3)- Border Pen Creation: This line creates a new pen for drawing borders. The color depends on the state of the bit (
Bits(i)), and the pen width is set based onBitSpacing.
borderPen.Alignment = Drawing2D.PenAlignment.Outset- Pen Alignment: This line sets the alignment of the pen to be outside the rectangle, ensuring the border appears correctly.
Graph.DrawRectangle(borderPen, Rect)- Draw Rectangle: This line draws the border around the rectangle using the created pen.
End Using- End Using Block: This line indicates the end of the
Usingblock for theborderPen, ensuring proper resource management.
End If- End If Statement: This line marks the end of the conditional check for hover.
Next- End Loop: This line marks the end of the
Forloop, which will continue to the next index until all bits are processed.
End Sub- End Subroutine: This line indicates the end of the
DrawBitBoxessubroutine.
The DrawBitBoxes subroutine visually represents the bits of a byte as boxes filled with colors, displays the binary digits inside the boxes, shows their place values above, and highlights the box when hovered over. This code is part of a graphical user interface that helps users visualize binary data in a clear and interactive way.
Private Sub DrawActiveValuesBreakdown()- Shows which place values are active and their sum.
Private Sub CreateSoundFiles()
Private Sub CreateFileFromResource(filepath As String, resource As Byte())- Extracts embedded sound resources and saves them as
.mp3files.