Author: Hevanafa

  • JMBlue Catch Game

    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

  • QBJS Quirks Cheat Sheet

    I’m adding this to share my thoughts, especially the quirks that QBJS 0.10.0 has at the time of writing.

    Constants

    All consts 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)

    Variables

    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

    Known Issue

    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 Handling

    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

    Non-existent image

    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!

    Precompiler

    $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

    Console / Debugging

    $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

    Function Return Value

    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

    Module System

    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

    JavaScript Interop

    (TBA)

    Sound Handling

    (TBA)

    MapTriangle

    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

    ByRef or ByVal?

    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

    Boolean value

    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 QBJS

    The 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

    About _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

    About copying a UDT

    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
  • Beneath the City Lights (Zombie Girl Game)

    Beneath the City Lights (Zombie Girl Game)

    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.

  • hevanafaslime.com is back!

    hevanafaslime.com is back!

    Even though my domain is back, I don’t know how to recover the contents from the old cPanel yet. Stay tuned for some fun retro programming tutorials!

    Meanwhile, have my drawing of the site’s mascot Heva :3