Your canvas is a coordinate grid
Game Window "My Game" Size 640 By 360 creates a graphical program with a logical canvas. X increases rightward and Y increases downward. Window scaling does not change those logical positions.
Run the motion lab
This complete program needs no images, audio, or library imports. The arrow keys move a circle. Clicking inside the canvas increments a counter.
Option Explicit
Dim PlayerX As Number
Dim PlayerY As Number
Dim Clicks As Number
Dim Key As Number
PlayerX = 320
PlayerY = 180
Game Window "SMILE Motion Lab" Size 640 By 360
Do
Get Key Key
If Key_Held(KEY_LEFT) Then
PlayerX = Max(20, PlayerX - 4)
End If
If Key_Held(KEY_RIGHT) Then
PlayerX = Min(620, PlayerX + 4)
End If
If Key_Held(KEY_UP) Then
PlayerY = Max(20, PlayerY - 4)
End If
If Key_Held(KEY_DOWN) Then
PlayerY = Min(340, PlayerY + 4)
End If
If Pointer_Inside() And Pointer_Pressed(POINTER_PRIMARY) Then
Clicks = Clicks + 1
End If
Clear Rgb(10, 18, 36)
Fill Rectangle 20, 70, 600, 250, Rgb(17, 39, 60)
Fill Circle PlayerX, PlayerY, 16, CYAN
Draw Circle PlayerX, PlayerY, 20, WHITE
Draw Text "Arrow Keys Move - Click Counts - Escape Exits" At 320, 20 Size 18 Color WHITE Centered
Draw Text "Clicks:" At 24, 45 Size 16 Color YELLOW
Draw Number Clicks At 95, 45 Size 16 Color YELLOW
Show Screen
Wait 16 Milliseconds
Loop Until Game_Closed() Or Key = KEY_ESCAPE
End ProgramDownload the complete motion lab. The Min/Max calls keep the circle inside the canvas. This first lesson uses a simple four-pixels-per-loop speed; a steady-speed game should scale movement by elapsed time or use a fixed-step update.
Build one complete frame
Each pass through the loop reads input, updates state, redraws the scene, and presents it. Show Screen also ends the pointer-input frame. Read pressed/released buttons and movement deltas before that call. Wait 16 Milliseconds adds a short pause; rendering work also takes time, so it does not guarantee exactly 60 frames per second.
Draw with shapes and text
The following commands go after Game Window. Positions, sizes, radii, and colors are Number expressions. Draw outlines a shape; Fill paints its interior.
Clear Rgb(10, 18, 36)
Fill Rectangle 40, 60, 180, 90, DARK_BLUE
Draw Rectangle 40, 60, 180, 90, LIGHT_BLUE
Fill Rounded Rectangle 260, 60, 180, 90, 18, DARK_GREEN
Draw Rounded Rectangle 260, 60, 180, 90, 18, LIGHT_GREEN
Fill Circle 500, 105, 35, CYAN
Draw Circle 500, 105, 42, WHITE
Draw Line 40, 190, 580, 190, ORANGE
Draw Arc 500, 265, 40, 0, 270, YELLOW
Fill Quadrilateral 40, 230, 180, 245, 150, 315, 60, 300, MAGENTA
Draw Text "Level 1" At 320, 20 Size 24 Color WHITE Centered
Draw Number 100 At 40, 330 Size 18 Color YELLOW
Show Screen| Shape | Arguments |
|---|---|
| Rectangle | X, Y, Width, Height, Color |
| Rounded Rectangle | X, Y, Width, Height, Radius, Color |
| Circle | CenterX, CenterY, Radius, Color |
| Line | X1, Y1, X2, Y2, Color |
| Arc | CenterX, CenterY, Radius, StartAngle, SweepAngle, Color |
| Quadrilateral | X1, Y1, X2, Y2, X3, Y3, X4, Y4, Color |
Arc angles use integer degrees: 0 points right and 90 points down. Positive sweep is clockwise; negative sweep is counterclockwise. Draw Arc draws only the curve. There is no Fill Arc statement.
For a HUD label, Text_Width("Level 1", 24) and Text_Height("Level 1", 24) measure the drawn text. Measurement needs a Game Window and can vary slightly between drawing backends.
Pressed once or held down?
| You want… | Use | Example |
|---|---|---|
| One action from a queued key press | Get Key PressedKey | Start, pause, or choose a menu item; no queued key gives KEY_NONE. |
| Continuous movement | Key_Held(KEY_RIGHT) | Keep moving while Right is held. |
| Modifiers at the moment of a queued key event | Key_Event_Held(KEY_CONTROL) | Read immediately after Get Key to preserve a quick Control+Arrow press. |
| One click in the current frame | Pointer_Pressed(POINTER_PRIMARY) | Place a marker or select a button. |
| Dragging or wheel input | Pointer_Held(...), Pointer_Delta_X(), Pointer_Wheel_Delta() | Move a map view or adjust zoom. |
Pointer X/Y coordinates use the same canvas as drawing. Check Pointer_Inside() before treating a location as a canvas click. Show Screen clears frame deltas and pressed/released transitions, while held state remains.
Load a picture once, draw it many times
Declare an Image, load it before the game loop, and check Image_Loaded. Draw it inside the loop. Unload it when its owner no longer needs it.
Dim HeroImage As Image
Load Image HeroImage From "Assets/Hero.png"
If Image_Loaded(HeroImage) Then
Draw Image HeroImage At 200, 120 Size 96 By 96 Filter Pixel
End If
Show Screen
Unload Image HeroImageThis is a lifecycle fragment: add a Game Window, provide the PNG, and place the drawing in your loop. PNG transparency is preserved. Filter Pixel gives crisp pixel art; Filter Smooth suits paintings and high-resolution illustrations. Assignment shares the decoded image resource safely; clearing one Image variable does not unload another variable's retained image.
Draw Image Sheet From 32, 0 Size 32 By 32 At 200, 120 Size 96 By 96 Filter Pixel
Draw Image Sheet From 32, 0 Size 32 By 32 At 300, 210 Size 96 By 96 Anchor 48, 96 Opacity 80 Filter Pixel Flip HorizontalAnchor 48, 96 puts the destination image's bottom center at the requested X/Y point. Opacity runs from 0 to 100. Flipping preserves the anchor. Image rotation is not part of this 2D Draw Image syntax.
Keep drawing inside a panel
Clip Rectangle 40, 80, 240, 160
Draw Image HeroImage At 0, 0 Size 480 By 480
End ClipNested clips intersect. Leaving a clip scope restores the previous clipping region, including when a Return or loop exit leaves the block.
Tell the project which assets it owns
Keep asset paths relative to your project, with consistent letter case. Add files to the application's project asset list so native and Web builds can publish them beside the program.
<ItemGroup>
<Asset Include="Assets/Hero.png" />
<Asset Include="Assets/Collect.wav" />
<Asset Include="Assets/Level.mp3" />
</ItemGroup>Use those same logical paths in SMILE code. Absolute paths, URLs, paths that escape the project, and undeclared project assets are rejected. Libraries supply code; applications supply assets. Loose-file compilation has no project asset manifest, so you must place the assets beside the generated executable yourself.
Sound effects and music have different jobs
Play Sound "Assets/Collect.wav" On Channel 1
Play Sound "Assets/Impact.wav" On Channel 2
Stop Sound On Channel 1
Play Music "Assets/Level.mp3" Loop
Music Volume 50
Pause Music
Resume Music
Stop Music
Stop SoundThese are separate command examples, not a sequence to run all at once. Start music when active play starts, keep it looping during that play session, and stop it when returning to the title or exiting. Play effects when events happen, such as collecting a coin.
There are 16 effect channels, numbered 0–15. Bare Play Sound uses channel 0; another sound on the same channel replaces the old sound. Different channels overlap. Bare Stop Sound stops all effects. Music Volume uses 0–100.
Native focus handling automatically silences game audio while inactive and stops effects. A manually paused music track remains paused. Browser audio may require a player interaction before playback; Web focus behavior pauses requested music while the page is hidden and restores it when appropriate.
Save a score, then graduate to structured data
For one whole-number value, a named save key is enough. The key belongs to the application's persistent storage; it is not an asset filename.
Option Explicit
Dim Best As Number
Dim Score As Number
Score = 250
Load Best From "HighScore" Default 0
If Score > Best Then
Best = Score
Save Best To "HighScore"
End If
Print "Best: "; BestFor a custom save layout, store bytes in a fixed Number array. Always inspect Status before announcing that data was saved.
Option Explicit
Dim Bytes[8] As Number
Dim ByteCount As Number
Dim SaveStatus As Number
Bytes[0] = 3
Bytes[1] = 25
Save Data Bytes Count 2 To "PlayerProfile" Status SaveStatus
If SaveStatus = DATA_STATUS_OK Then
Print "Saved"
End If
Load Data "PlayerProfile" Into Bytes Count ByteCount Status SaveStatus
If SaveStatus = DATA_STATUS_OK Or SaveStatus = DATA_STATUS_RECOVERED Then
Print "Loaded bytes: "; ByteCount
End IfEach byte must be 0–255; the count must fit the array and the 1 MiB data-block limit. The status distinguishes a missing save, invalid data, corruption, unavailable storage, recovery, and excess size. Native storage is application-owned; Web storage is isolated by application identity and can be removed by browser storage controls. Use Smile.RPG.SaveGames for that library's structured RPG progress.
Read packaged text; export player-chosen text
Dim FileBytes[8192] As Number
Dim FileByteCount As Number
Load Text File "Maps/level.map" Into FileBytes Count FileByteCountLoad Text File gives you UTF-8 bytes, not a Text value or a parsed map. It skips an optional UTF-8 BOM, zero-fills the array, and returns zero count for a missing/unreadable file. Use Smile.Game.TileMap when you want the built-in package's map format.
Dim Exported As Boolean
Dim Imported As Text
Exported = File_Export("my-notes.txt", "My game idea")
Imported = File_Import()Call file-transfer functions in response to an explicit player action such as a menu choice. Import cancellation and an empty file both return empty Text. These functions transfer user-chosen UTF-8 files; they do not modify your Save Data automatically.
Keep platform differences in view
| Feature | Practical boundary |
|---|---|
| 2D graphics | Shared logical coordinates and syntax; actual text measurements can differ between native and browser drawing engines. |
| 3D rendering | Native DirectX and WebGL2 provide true 3D. GDI is a wireframe/2D fallback. The newest arena/VFX lessons document native behavior; their browser adoption is not claimed here. |
| Input | Browser keyboard input belongs to the focused canvas. Native KEY_PLUS, KEY_MINUS, and KEY_C additions are not claimed as adopted in Web. |
| Window helpers | Window_Activate() and Window_CloseRequested() return False on Web; the browser owns focus and tab-close confirmation. |
| Persistence | Native application data and browser local storage are separate stores. Saving on one target does not synchronize the other. |
Make it yours
- Change the motion lab's colors and circle radius.
- Add a target square and increment a score when the circle reaches it.
- Play a WAV effect only when the score changes.
- Save the best score when the round ends.
Next: reuse movement, UI, and game-system libraries, or build a 3D arena with elemental effects.