I made this with QBJS (v0.10.1)
The image assets are made by @jmblue7827 on TikTok
Changelog
06-10-2025: Increased maximum fish to 30
I made this with QBJS (v0.10.1)
The image assets are made by @jmblue7827 on TikTok
06-10-2025: Increased maximum fish to 30
I’m adding this to share my thoughts, especially the quirks that QBJS 0.10.0 has at the time of writing.
All const
s in QBJS should not have sigil suffixes:
const maxHealth = 100
const vgaWidth = 320
' Valid in QB64 but not in QBJS
const maxHealth% = 100
const vgaWidth& = 320
The interpreter will throw an error if you add a suffix. An example error message is something like this:
ERROR : 0 : Missing initializer in const declaration
This is due to how dynamic typing is implemented in its backend language (JS)
This of QBJS as a QB64-to-JS transpiler with the full power of JS
This makes dynamic typing possible, which is similar to Lua
Some variables with the system
prefix may cause the transpiler to break
For example, with the name systemFont
being used as a variable:
dim as TBMFont systemFont
This will cause the transpiler to sometimes produce an incomplete object string in JS, but this only happens occasionally
If this happens, just reorder your variable declarations — there may be occasional ordering-dependent bugs in the QBJS-to-JS transpiler
Image handles start at 1000 instead of a negative value
With the same example code
Option _Explicit
Dim As Long imgGasolineMaid
imgGasolineMaid = _LoadImage("IMG\gasoline_maid_256px.png")
Print "image handle:", imgGasolineMaid
_FreeImage imgGasolineMaid
This is the comparison:
QB64: image handle: -10
QBJS: image handle: 1000
Loading a non-existent image is also possible, which is dangerous:
Option _Explicit
Dim As Long imgInvalid
imgInvalid = _loadImage("invalid_image.jpg")
Print "image handle:", imgInvalid
Output (QBJS):
image handle: 1000
This can be prevented with the _FileExists
statement
Option _Explicit
Dim As Long imgInvalid
If Not _FileExists("invalid_image.jpg") Then
Print "Invalid image doesn't exist!"
Else
imgInvalid = _LoadImage("invalid_image.jpg")
Print "image handle:", imgInvalid
End If
Output:
Invalid image doesn't exist!
$Let
doesn’t work with QBJS
$If
precompiler only works with either:
$If WEB then
$If Javascript then
Use $If WEB
to use the functionalities that only exist in QBJS
Option _Explicit
$if WEB then
import console from "lib\web\console.bas"
console.log "Hello QBJS!"
$else
print "Hello QB64!"
$endif
Use $If Javascript
block to execute any JS code in it:
Option _Explicit
Dim As Long a, b, c
$if javascript then
a = 1
b = 2
c = a + b
$endif
print c
Output:
3
This makes it possible to use fetch API and BigInt
along with QB64
$Debug
and any Log
statements (_LogError
, _LogWarn
, _LogTrace
, and so on) don’t work in QBJS
Instead, use the Console import
Option _Explicit
$if web then
import console from "lib\web\console.bas"
console.log "Hello QBJS!"
console.warn "This is a warning"
console.error "This is an error log"
$endif
This makes it possible to use console
in QBJS, but since everything is async
under the hood, using console.log
in Javascript context will throw an error
Since QBJS uses JavaScript as its backend, it can return an object, or even a UDT
Option _Explicit
Type TCat
name As String
colour As String
End Type
Dim cat As TCat
cat = newCat("Honey", "orange")
Print cat.name, cat.colour
' implementation
Function newCat(name$, colour$)
Dim c As TCat
c.name = name$
c.colour = colour$
newCat = c
End Function
This makes it similar to VBScript but with full browser support
QBJS is more like the OOP in JS, which makes it also similar to Lua
MAIN.BAS
import console from "lib\web\console.bas"
import YourModule from "modules\hi.bas"
YourModule.sayHi
print YourModule.add(2, 3)
MODULES\HI.BAS
option _explicit
export sayHi, add
sub sayHi
print "Hi from another module!"
end sub
function add(a, b)
add = a + b
end function
(TBA)
(TBA)
It’s not possible to use _MapTriangle
statement in QBJS, so drawing a rotated image + scaled image is still a dream at the moment of writing
QBJS always uses pass-by-value instead of pass-by-reference like what QB64 uses
Therefore, loadImage
in QBJS returns a value instead of assigning to the target image handle
The values true
& false
can be used immediately from JS
The AND
, OR
, and NOT
operators in QBJS use JS bitwise operators under the hood (Ref: https://qb64phoenix.com/forum/showthread.php?tid=3376)
import console from "lib\web\console.bas"
console.log "What is TRUE? " + i32str(1 > 0)
console.log "What is FALSE? " + i32str(1 < 0)
true
is 1, false
is 0
This makes casting to strings can be easier instead of using the usual STR$
console.log "What is TRUE? " + (1 > 0)
Not
operator in QBJSThe transpiler uses the ~
operator under the hood, which basically flips all the bits in the 64-bit integer. This makes it harder to track a boolean value & an actual integer
So, instead of using the usual
If Not isImageSet(imgHandle) Then Exit Sub
It’s better to use:
If isImageSet(imgHandle) = false Then Exit Sub
But in my case of returning an integer (supposedly), I’ll use an integer return value instead of a boolean. Something like this:
Function isImageSet% (imgHandle As Long)
isImageSet = -(imgHandle >= 1000)
End Function
true
is 1 in JS, while false
is the same: 0
_PutImage
Compared to QB64, it uses destX + destW - 1
and also destY + destH - 1
because of how the decrement is handled automatically under the hood
So this is the acceptable version in QBJS:
Sub sprRegion (imgHandle As Long, srcX%, srcY%, srcW%, srcH%, destX%, destY%)
_PutImage (destX%, destY%)-(destX% + srcW%, destY% + srcH%), imgHandle, , (srcX%, srcY%)-(srcX% + srcW%, srcY% + srcH%)
End Sub
UDT’s (User-defined Types) in QBJS are basically just a JS object
It doesn’t support copying as if it’s a record
in Pascal, since the object reference remains the same
I found this problem when calling loadBMFont, but the output is just ~~~~~~
, while it should be something like "Hello QBJS!"
but with PICO-8 font
So, instead of declaring tempGlyph
at the beginning like what I usually do in Pascal, something like this:
sub loadBMFont ' procedure signature
dim as TBMFontGlyph tempGlyph
' the rest of the implementation
end sub
It’s better to declare the variable inside the block it’s supposed to be
elseif startsWith(txtLine, "char") and not startsWith(txtLine, "chars") then
dim as TBMFontGlyph tempGlyph
' the rest of the implementation
end if
“Not retro for nostalgia. Retro for power”
A DOS-based game dev framework that lets you create authentic retro games with Turbo Pascal 7 along with modern tooling such as VSCode.
It’s not just retro, but it’s a 100% authentic retro development framework with DOS as the operating system.
The features are comparable with fantasy consoles like PICO-8 and TIC-80, which features inspired me to make something similar.
Another inspiration is Terry A. Davis’ TempleOS, which is an experimental OS that looks very similar to DOS but can be run on a 64-bit computer with more than 8 cores running at the same time.
A game made for the PixelGameJamYT with the theme “Light is Dangerous”
Game Jam Page
A game made for the Monochromatic Game Jam 1
Game Jam Page
(TBA)
copy_units.ps1
to copy UNITS folder into boilerplateQ: What are the system requirements?
A: When running with Turbo Pascal, I recommend you to use DOSBox-X with Pentium 60MHz (32090 cycles/ms) as the default CPU speed
Q: How to use with modern tooling such as VSCode?
A: When using VSCode, it’s best to use the Pascal extension and also DOSBox ready with Turbo Pascal open, so that you can immediately detect file changes & compile with F9 key in the IDE
Q: How to make & use my own fonts?
A: You can either use BMFont (use the text output) or Fony (save the font as FNT)
(TBA)
My submission for the Zombie Girl Game Jam held on itch.io the whole November!
You can either play the game here, or on my itch.io page :3
Rate my game for the game jam ratings: https://itch.io/jam/zombie-girl-game-jam/rate/3130966
The whole game was made within 10 days with Ruby, and the game engine is TIC-80. I had to buy the PRO version to get rid of the 64 KB code limit, but it was worth it xd (the whole codebase is in 1 file, which is almost 70 KB in size). Making a game with TIC-80 is both fun and challenging, but it’s mostly challenging due to the use of its virtual machine limitations.
WHen making the deadline version of the game, I didn’t have enough time to implement the whole storyline & the sound assets, so I focused on only making the first 2 days where (Spoiler alert!) Lilzy & Ethan reconcile.