Now that we have a ton of solid information on SmileBASIC 4, I think it's a good time for a guide to get people prepared! I've been thinking of ways to try and summarize the changes in the SmileBASIC 4 reference, but there's really a ton of stuff in there. I would highly recommend looking through it thoroughly, because a lot has changed!
But even for those who have looked through it, there may be stuff you've missed! It's hard to really digest the big picture information from an API reference. Which is exactly why I have digested it down for you, and am fully prepared to regurgitate this information for you like a bird to her young!
So, in this guide, I'm gonna go over the biggest changes I've noticed. I'll assume you're fairly familiar with SmileBASIC 3 for this breakdown. I'm also gonna be talking more about the big picture concepts, rather than specific functions, because I'm sure you'll be able to find this stuff in the reference once you know what you're looking for.
So, to start off, let's look at SmileBASIC 4's language changes, as well as some function changes and additions.
The first thing to understand here is that SmileBASIC 4 is not backwards compatible with SmileBASIC 3 (the 3DS version) or BIG (the Japan-only Wii U version). 3 and BIG managed to keep some level of cross-compatibility with each other with the aid of the XON statement, but SmileBASIC 4 makes a clean break from that. XON no longer exists at all, and even VERSION has been moved to a constant called #VERSION. Due to these and other major incompatible changes, it's not very feasible to write programs that work with both SmileBASIC 3 and SmileBASIC 4 without a large amount of system-specific code.
It is possible in SmileBASIC 4 to download SmileBASIC 3 projects from public keys. This is intended to aid porting efforts from SB3 to SB4. It is not possible to do the reverse, however.
In SmileBASIC 3, variable names have suffixes that determine their type.
In SmileBASIC 4, however, suffixes are basically meaningless. In fact, the only effect they have now on a variable is which default value it has (0.0, 0, or "") if you access it without assigning anything to it first (e.g. PRINT A without first doing A=123, assuming OPTION STRICT isn't set).
Thus, in SmileBASIC 4, only values themselves have types; any value can be assigned to any variable. In other words, this is now perfectly valid:
To some of you this may seem like a lawless hellscape from which we can never escape, but this isn't much different from any other scripting language. To compensate for variables being untyped, we now have more tools for dealing with value types:
TYPEOF(value) returns the type of any given value, including arrays.
INT(value), FLOAT(value), and STR$(value) convert the given value to that particular type. (STR$ isn't new, but it's here for completeness.)
INSPECT value, or ??value for short, gives you detailed information about a value, including what type it is, its contents if it happens to be an array, and so on.
However, if you were hoping for user-defined types, no luck, I'm afraid. The full list of types is the same as in SmileBASIC 3: the null type, integers, floats (or "real numbers"), strings, integer arrays, float arrays, and string arrays.
(If you're wondering about null, it's not really important. The main cases you'll see it are if you don't pass a parameter you're supposed to (FOO A,,C instead of FOO A,B,C) or don't return a value when you're supposed to (DEF FOO():RETURN:END).)
In SmileBASIC 4, however, the rules work like this:
Decimal number literals with a decimal point (like 123.), or scientific notation syntax (like 5E10), are always floats regardless of value.
Other decimal number literals (like 123) are ints, or floats if they can't fit in an int.
Hexadecimal or binary number literals (&H123 or &B1000) are always ints. If they cannot fit in an int, a syntax error occurs.
If both operands are ints, the +, -, and * operators result in an int, or a float if overflow would occur. If either operand is a float, they always result in a float.
The / operator always results in a float.
Unary - and ! always result in the same type they're used on.
All other operators always result in an int.
Ints and floats are never implicitly converted to each other when assigning to a variable except when assigning to an array of the other number type.
One thing SmileBASIC 4 does add is that arrays are a bit nicer to initialize:
This is special syntax for VAR/DIM, rather than an actual array literal, though.
LAST(array) is equivalent to LEN(array)-1. It's a small thing, but it makes iterating over arrays a little nicer:
FOR I=0 TO LAST(MY_ARRAY)
It is now possible to determine the number of dimensions of an array, as well as the size of each dimension:
PRINT DIM(MY_ARRAY) '3
PRINT DIM(MY_ARRAY,1) '7
Multi-dimensional arrays can now be referenced as if they were one-dimensional. However, functions that resize an array, like PUSH, cannot be used with them. They can be explicitly resized with the new RESIZE command, however.
The new INSERT and REMOVE commands allow you to insert and remove elements from a one-dimensional array.
The new function form of COPY lets you copy a portion of an array without having to declare a new variable. It works on strings as well, where it's equivalent to MID$.
INSPECT COPY(MY_ARRAY,2,3) '[2,3,4]
The ARRAY#, ARRAY%, and ARRAY$ functions create arrays of a specified size, filled with the default value for that type. For example, ARRAY%(5,5) makes a two-dimensional 5 by 5 array, filled with 0.
Another new thing SmileBASIC 4 has is variadic functions, which can take any number of arguments, and return any number of values. Among other things, this allows you to make functions that take optional arguments!
DEF DOUBLE_ALL * OUT *
IF DEFARGC() != DEFOUTC() THEN
STOP "Number of inputs and outputs must match"
FOR I=0 TO DEFOUTC()-1
DEFOUT I, DEFARG(I)*2
DOUBLE_ALL 1,2,3 OUT A,B,C
PRINT A,B,C '2 4 6
Arguments and return values are accessed like this:
DEFARGC() gets the number of arguments passed in.
DEFARG(n) gets the nth argument.
DEFOUTC() gets the number of return values expected.
DEFOUT n, value sets the nth return value.
A couple of new control flow statements have been added:
PRINT "N is three!"
PRINT "N is seven or eight!"
PRINT "N isn't three, seven, or eight."
Constants and enums
It's now possible to define your own constants and enums:
'#A=100, #B=101, #C=102, #D=200, #E=201
Lines can be continued onto a new line by adding a backslash to the end of the line:
FOO A,B,C, \
'is equivalent to
For that matter, backslash is now an actual backslash character, not a yen symbol like you might see above.
ON BREAK GOTO @LABEL allows you to jump to a label when the user attempts to close a program with the + Button. Although they can still force a program to close by holding the + Button, this allows you to do things like confirm whether the user wants to save a file first.
The new functions PERFBEGIN and PERFEND allow you to measure the amount of time elapsed between them, in microseconds. Up to eight measurements can run at once, and these measurements can be drawn on a performance graph.
Loading and saving files is a bit different now. LOAD and SAVE have each been split into three different variants:
LOAD/SAVE is for moving programs between files and slots. This replaces LOAD/SAVE "PRGn:name".
LOADG/SAVEG is for moving graphics between files and GRPs. This replaces LOAD/SAVE "GRPn:name".
LOADV/SAVEV is for moving data between files and variables. This replaces LOAD/SAVE "type:name".
Filename prefixes in SmileBASIC 3 did double duty as targets for LOAD/SAVE ("PRGn:" and "GRPn:") as well as signifying file types ("TXT:", "DAT:", and "GRP:"). In SmileBASIC 4, file types are the only filename prefixes.
Another interesting thing to note is that GRP files no longer have to be the size of an entire graphics page. Also, DAT and GRP files are automatically compressed when saved. Considering that a full uncompressed graphics page would weigh in at 16 MB, these are very welcome changes!
In SB3, there was the SYS project, which contains built-in files like the default spritesheet. In SB4, there are instead multiple system projects, like #SYS or #GUIDE, with names starting with #. Any file in a system project can be loaded or executed from any other project (e.g. LOADG "#SYS/DEFFONT",5).
Subprograms are the replacement for SB3's tool programs. Only one subprogram can be running at a time, but unlike in SB3, they can display stuff in their own window, overlapping the screen, and run in parallel with the editor or main program. They're able to read and write the GRPs and program slots of the main program, and can get some basic status information, like whether the editor is open, or whether the program has reached an error, and so on.
Subprograms can be run at any time using the F9-F11 keys on a USB keyboard, by using the Smile buttons in the software keyboard, or by binding a button combination to them. They can even be started while a program is running. For example, this means that the graphics editor can be used to modify the graphics of a program while it's still running! ENOCODE, a Scratch-like editor tool, is an example of a subprogram. In fact, it seems like components of the UI itself are special subprograms, such as the software keyboard!
Whether the main program or subprogram receives input depends on which one has focus. When a subprogram starts, it is given focus, and it can give and take it away at will. However, the main program does not have this power; it can't take focus from the subprogram, nor give it back once given it.
This does pose a problem, if you need a subprogram that needs to stay open to preserve state, but also need to be able to switch focus between the main and subprograms freely. After all, the subprogram can give focus to the main program, but how will it know to take focus back if it can't get any input?
Well, it turns out that's not entirely true. At least for the time being, subprograms can still read the USB keyboard and mouse cursor position (though not the mouse buttons). There's also some undocumented special character codes PUSHKEY can use to switch focus from the main program, but I wouldn't rely on that.
Of course, if you don't need to preserve state, you can simply close and open the subprogram as needed, without having to use any dodgy undocumented behavior.
METALOAD, METAEDIT, and METASAVE allow you to edit the title, description, and icon of the current project.
System variables are now completely gone. Specifically:
TRUE and FALSE are now #TRUE and #FALSE (which already existed in SB3).
FREEMEM, RESULT, MAINCNT, MILLISEC, TIME$, DATE$, CALLIDX, and PCMPOS are now functions of the same name.
VERSION and HARDWARE are now #VERSION and #HARDWARE.
TABSTEP and SYSBEEP are now accessed via SYSPARAM.
CSRX, CSRY are now accessed via LOCATE OUT X,Y.
CSRZ is now the Z-coordinate returned from TOFS OUT X,Y,Z.
ERRNUM, ERRLINE, and ERRPRG are removed, perhaps because programs couldn't really use them anyway, and LIST ERR already makes the editor jump to the location of the error. Subprograms can get this information with ENVSTAT.
MICPOS and MICSIZE are removed because SmileBASIC 4 has no support for microphones.
MPCOUNT, MPHOST, and MPLOCAL are removed because SmileBASIC 4 has no support for local wireless multiplayer, instead opting for multiple controllers on a single console.
The special constants #_LINE, #_SLOT, and #_FILENAME give the current line number, slot, and filename where they are used.
Putting stuff on the screen
Alright, so let's look at how things have changed since SmileBASIC 3 when it comes to displaying stuff, because there's a lot more than you might think!
Graphics pages, or GRPs for short, are still pretty much the same. Instead of being 512x512, they're 2048x2048, and instead of RGBA1555 color, it's ARGB8888. That means you have the full RGB(0,0,0) to RGB(255,255,255) range of color, plus a proper alpha channel this time!
In SmileBASIC 3, you have six normal pages, GRP0 through GRP5, and then you have a special one called GRPF for the font.
In SmileBASIC 4, you have six normal pages, GRP0 through GRP5, but this time GRPF is actually just another name for GRP5. In addition, there's GRP-1 (that's negative one) which can't be written to and is completely white (specifically, &HFFFFFFFF). To tell you the truth, I'm actually not entirely sure where GRP-1 would come in handy... but hey, it's there if you need it for something!
So, in SmileBASIC 3, there are four main ways to put stuff on the screen:
The graphics screen, which displays the contents of a graphics page, by default GRP0.
Backgrounds, or BGs for short, which are made up of tiles, which by default come from GRP4. There are four BGs in all.
Sprites. Everyone knows what sprites are, right? By default, their graphics come from GRP5. There are 512 sprites in all.
The text screen, where text is printed. The font comes from GRPF.
Well, in SmileBASIC 4, it's pretty different. There are only two main ways:
Sprites. They're no longer tied to a single graphics page; each sprite can choose a region from any page. However, by default, they all come from GRP4, starting at coordinates (0, 0). There are 4096 in all.
The text screens. Yep, screens. There's five of them now. Most of the font comes from GRPF, which is now just another name for GRP5, but there's also 4096 "user-defined characters" from &HE800 to &HF7FF. By default, these also come from GRP4, starting at coordinates (1024, 0).
"Huh?" you might be asking. "Where'd the graphics screen go? Where's the backgrounds? Are they just... gone?"
Well... not exactly. I mean, yeah, they are, but only because they're not needed in the first place! Let me explain:
The "graphics screen" is now just a huge sprite covering the entire screen. Specifically, sprite 4095, which also has a handy constant, #GSPRITE. That sprite is set to use (0, 0) to (w, h) of GRP0, where w and h are the screen width and height. In other words, if you want to put something on the screen, you just... draw to GRP0. Just like you always did!
The four "backgrounds" are now just text screens 0-3, and the one you normally print text to is text screen 4. Yep, the text screen was basically just a crappier BG anyway, so why not? Naturally, to compensate, text screens can now do everything BGs used to do, so you can rotate them, scale them, and so on, and you can choose between 8x8 and 16x16 fonts for each. Those "user-defined characters" I told you about earlier? Yep, those are your tileset!
So, just to summarize, in bold letters for those people in the back: The graphics screen and BGs have been merged with sprites and text screens respectively, and thus have been rendered obsolete.
Pretty crazy, right? BG tiles are just text now! You could stick 'em in strings if you wanted to! Maybe even put a whole map in DATA statements? The graphics screen is just a sprite, so you can rotate and scale it as much as you want!
Something else to be aware of is that just because there's only 4096 "user-defined characters" doesn't mean you only have 4096 tiles to work with! After all, you have the whole rest of the font to work with. If you want to sacrifice stuff like kanji for more tiles, you can do that!
Anyway, adjusting from SmileBASIC 3 to SmileBASIC 4 isn't too bad here. A lot of the BG* functions have T* equivalents, and you'll still use the same old G* functions to draw to GRP0.
But that's not all! We've also got layers! What's a layer? Well...
There are 8 layers, from 0 to 7. Like the name implies, they're drawn stacked over each other, with layer 0 being the top layer, and layer 7 being the bottom layer. Each sprite and text screen can be assigned to one of these layers. Regardless of Z-coordinate, the stuff in layer 0 will always be drawn over the stuff in layer 1, and so on.
But, of course, that's not all layers are good for. For starters, you can move an entire layer as a single unit. But not just moving it--with LMATRIX, you can apply an entire 4x4 transformation matrix to each layer! That's right, not just translation, but rotation, scaling, and skewing, all in 3D space! Mode 7-like effects are a breeze now!
But that's not all! With LFILTER, you can pixelate, blur, and change the hue, saturation, and value of the entire layer!
But that's not all! LFILTER also lets you create raster effects by changing the X scroll, Y scroll, X scale, and Y scale of every individual row or column of the layer's pixels! Wavy effects? No problem! And sorry, did I say Mode 7-like effects earlier? Sorry, I meant just straight-up Mode 7! That spinny cylinder room in Super Castlevania IV that was really cool but also made you a bit nauseous? Yeah, you can do that now.
Then there's color blending with LAYER, which lets you do composition by adding or multiplying colors!
Finally, clipping is now part of layers, via LCLIP, rather than sprites or text screens.
So, the screen is made up of layers, and the layers are made up of sprites and text screens! Neat! What's next?
The screen itself
Well, now that you understand how the screen is put together, let's finish things up by talking about the screen itself!
Good old XSCREEN is still around, but it works a little differently now:
The first two parameters are the width and height of the screen. Width can be from 128 to 1280, and height can be from 128 to 720. You can choose any number divisible by 4 that's within those ranges. The default resolution is 400x240, the same resolution as the 3DS's top screen. Actually, everything after this is optional, so you could just write XSCREEN 1280,720 if you wanted to, but let's take a look at the rest of them anyway!
The next parameter is sample magnification. What does that do? Well, think about this: Say you want a real low-resolution game, something crazy like the Game Boy's 160x144 resolution. But of course, you still want those fancy sprite rotation and scaling effects. So you rotate a sprite a few degrees... and it turns into a mess of pixels. Well, of course it does, you're rendering at 160x144! But maybe you didn't necessarily want to render everything at 160x144. Maybe you still wanted rotation and scaling to be buttery-smooth, even if it's not totally sticking true to that resolution.
Well, that's what sample magnification is for! If you set it to, say, 3, everything will still be as if it were rendered at 160x144. But when you start rotating and scaling stuff, you'll see that it's actually rendering at 3x that resolution, at 480x432. That said, you can't get a resolution greater than 1280x720 this way, nor can you set the magnification lower than 1.
The next parameter is the screen's texture filtering. 0 is bilinear filtering (blurry pixels), 2 is nearest neighbor (sharp pixels), and 1 is somewhere between the two.
Finally, the last parameter is the aspect ratio. This lets you stretch the screen, if you're in the mood for some rectangular pixels!
So, that about covers it! If you thought there wasn't much of a change when it comes to putting stuff on the screen, you haven't been digging deep enough!
Well, now that we've covered output, the next thing we should cover is input! A lot has changed here too, though a bit of it might be familiar if you ever used SmileBASIC BIG.
Buttons and sticks
The 3DS has built-in buttons, but the Switch has controllers to worry about. Are you playing with a Pro Controller? Or maybe two Joy-Cons? What about a single Joy-Con? And what about multiple players?
Because of that, a lot has changed here, so if you try and use BUTTON like you used to, things aren't going to work like you expect. It's hard to explain the differences in text, so let's look at some code.
Now, in SmileBASIC 3, if you wanted to check if A was pressed this frame, you'd do something like this:
IF BUTTON(2) AND #A THEN
PRINT "A was pressed!"
In SmileBASIC 4, however, it's like this:
IF BUTTON(0,#B_RRIGHT,2) THEN
PRINT "A was pressed!"
Now, what on Earth is that? Well, let's look at what's changed:
The first parameter is the controller ID. 0 is the "default controller", whereas 1-4 are players 1 through 4. We'll get back to this later on when we talk about setting up controllers, but for now, sticking with 0 is fine.
Also, the constants for buttons have changed. The first thing to be aware of is that these constants don't refer to specific physical buttons, but rather how these buttons are used while playing. For example, when you play with a single right Joy-Con, you hold it horizontally, so you move with the "left stick", and you press the "A button" to confirm things, even if physically speaking these are technically the right stick and X button.
#B_LUP, #B_LDOWN, #B_LLEFT, and #B_LRIGHT represent the buttons you'd use as a D-Pad for your particular controller. If you're playing with a single Joy-Con, you don't have these at all, since you only have a stick to move with.
#B_RUP, #B_RDOWN, #B_RLEFT, and #B_RRIGHT represent the buttons you'd use as X, B, Y, and A respectively. These directions are according to how you'd hold the controller while playing, so for a single right Joy-Con held horizontally, #B_RRIGHT would actually be the button labeled as X, and so on.
Update: #B_X, #B_B, #B_Y, and #B_A now also work as aliases for the above.
#B_L1, #B_R1, #B_L2, and #B_R2 represent L, R, ZL, and ZR on the two Joy-Cons or a Pro Controller. Yeah, I know, L1 and L2... weird, right? This ain't no PS4!
#B_SL, #B_SR, #B_S1, and #B_S2 represent SL, SR, L/R, and ZL/ZR on a single left or right Joy-Con. These are actually aliases for the previous set of constants, in the same order.
#B_LSTICK represents clicking the left stick. Both single Joy-Cons have this, since the stick is at the left when you hold it while playing.
#B_RSTICK represents clicking the right stick. Single Joy-Cons don't have this at all.
#B_RANY represents pressing any of the ABXY buttons.
#B_LANY represents pressing any of the directional buttons.
#B_ANY represents pressing any button, except for clicking the sticks.
The last thing to be aware of is that these constants aren't bitmasks, they're button IDs counting from 0, 1, 2, 3, and so on. There is still a way to use bitmasks, though, which we'll get to shortly, and getting the bitmask for a button is as simple as 1<<#B_RRIGHT, so don't worry too much.
The last parameter should be pretty familiar, since it's the same as SmileBASIC 3.
0 checks if the button is currently held. This is the default if you omit the parameter.
1 checks if the button was pressed this frame, including BREPEAT.
2 checks if the button was pressed this frame, without BREPEAT.
3 checks if the button was released this frame.
So, probably the most notable part of this change is that you don't really have to understand bitmasks anymore to deal with buttons. If you want to check if both ZL and ZR are being held down:
IF BUTTON(0,#B_L2) && BUTTON(0,#B_R2) THEN
PRINT "ZL and ZR are being held!"
Of course, some of you probably like getting a bitmask of all of the buttons at once, and you can still do that by passing in -1 instead of a button:
IF BUTTON(0,-1,2) AND 1<<#B_RRIGHT THEN
PRINT "A was pressed!"
Also, BUTTON(controller, -1, 0) can be shortened to simply BUTTON(controller):
IF BUTTON(0) AND (1<<#B_L2 OR 1<<#B_R2) THEN
PRINT "ZL and ZR are being held!"
Next, there's BREPEAT, which is basically the same as in SmileBASIC 3. For some reason, you actually can't set BREPEAT on individual controllers. If you set it, it applies to all connected controllers, as explicitly noted in the reference. Now, in SmileBASIC 3, BREPEAT had its own weird set of button IDs you had to use, which were different from the button constants. Since the constants are now IDs instead of bitmasks, can you just use the constants now? Well, I don't know! The reference doesn't say.
Finally, let's talk about the analog sticks. They haven't really changed much. STICK OUT X, Y is now STICK controller[, stick] OUT X, Y. Stick 0 is the left stick, and 1 is the right stick, so EXSTICK is gone now. If you leave it out, it'll default to 0. And of course, just like the button constants, this takes the type of controller into account, so the stick will work as you expect when holding a Joy-Con sideways.
Anyway, that's how buttons and sticks work. Whew! Who knew they could be so troublesome?
Setting up controllers
So, that's all well and good, and if you're not doing anything special, you can get away with using the default controller. However, special features like motion controls, vibration, and the IR camera require that you use controllers 1-4, rather than 0, and of course they will fail if the controller you happen to be using doesn't have those functions. So, let's talk about detecting controllers, as well as requesting a specific style of controller.
So, there's a few different kinds of controllers, but they all fit into four different styles:
Full controller. Handheld mode, a pair of Joy-Cons, or a Pro Controller can all be used as full controllers. All buttons and both sticks are available, as well as motion controls and vibration. However, they are considered a single unit, so in the case of a pair of Joy-Cons, the two gyroscopes and accelerometers cannot be accessed independently.
Pair of controllers. Only a pair of Joy-Cons can be used as a pair of controllers. Pairs of controllers can do everything full controllers can, as well as access the two gyroscopes and accelerometers independently.
Sideways controller. Only a single Joy-Con or a Pro Controller can be used as a sideways controller. Only the left stick, ABXY buttons, and all four trigger buttons can be accessed, as well as the gyroscope, accelerometer, and vibration.
Sideways controller (strict). Same as the above, but only a single Joy-Con is permitted.
XCTRLSTYLE lets you request a specific controller style, and the Switch's controller connection screen will appear automatically if the wrong style of controller is connected. It also lets you enable use of the gyroscope/accelerometer and the IR camera.
You can also get information about connected controllers using the CONTROLLER function. This is useful if you want to support different kinds of controllers, falling back gracefully instead of demanding a specific style. CONTROLLER(controller) can tell you whether the given controller is disconnected, Joy-Cons connected to the Switch in portable mode, a Pro Controller, a pair of Joy-Cons, a single left Joy-Con, or a single right Joy-Con. Not only that, you can even get the colors of each controller! CONTROLLER(0) will always appear as portable mode, though.
Motion controls and vibration
Accessing the gyroscope and accelerometer isn't much different from SmileBASIC 3. There's no more XON and XOFF, since motion controls are enabled and disabled via XCTRLSTYLE now. GYROA, GYROV, ACCEL, and GYROSYNC are all still here, but now they all take a controller ID (and optionally a sensor number) as arguments. The sensor number is used to access the left and right Joy-Con sensors independently, where applicable.
Vibration is accessed with the VIBRATE command. It lets you change the frequency and amplitude of both the left and right vibration motor, or both, in the case of a sideways controller.
The IR camera
The IR camera is pretty complicated. Basically, there are three modes:
In clustering mode, up to 16 bright areas are recognized and returned as variable-sized rectangular regions called clusters. The position, size, number of pixels, average brightness and center of gravity is given for each cluster.
In shooting mode, images are taken. However, probably for the same reason that SmileBASIC 3 couldn't use the 3DS cameras, the actual image data cannot be accessed by a BASIC program. Instead, using IRSPRITE, a sprite can be assigned to a special GRP containing this data, which cannot otherwise be accessed. The image can be displayed on-screen this way.
In moment mode, the camera's view is split into 8 by 6 equal-sized regions called blocks. Average brightness and center of gravity is given for each block.
Other parameters like camera gain, exposure, and so on can be adjusted.
The touch screen
There's two key differences when it comes to the touch screen in SmileBASIC 4. The first is that it's not always available, because the touch screen is inaccessible while docked. The second is that the touch screen supports multitouch this time around! Up to 10 points can be tracked at once.
Keyboard and mouse
Yet another thing SmileBASIC 4 supports is USB keyboards and mice.
If you just want text input, you can still use INPUT, LINPUT, and INKEY$ like in SmileBASIC 3.
However, if you want to access the keyboard state directly, that can be done with KEYBOARD, using scan codes from the table starting on page 53 of the USB HID Usage Tables. Using KEYBOARD, you can check whether each individual key is being held down, or was pressed or released this frame. For example, you can check if the A key was pressed this frame like so:
IF KEYBOARD(4,2) THEN
PRINT "The A key was pressed!"
Accessing the mouse is done using MOUSE for the mouse and wheel position, and MBUTTON for the state of up to five mouse buttons. Again, you can determine whether each button is being held down, or was pressed or released this frame.
Nintendo Labo Toy-Cons
Yep. There's support for that too! I'm not gonna go into much detail about these, since these are probably especially niche, and this section's kinda dragging as it is, but just know that there's special support for the Piano, House, Motorbike, Fishing Rod, Robot, and Visor Toy-Cons.
And I believe those are all of the ways to get input in SmileBASIC 4. That was even longer than the graphics section!
To be... or not to be?
Really, at this point, the only thing left to cover is audio, and I don't think much has changed there. For that matter, it's not really my strongest subject... So, this might be the end!