Codetown ::: a software developer's community
Michael Levin and I grabbed coffee a few weeks ago discussing what we might do with Orlando JUG (thanks to gmail for the offer for a TornadoFX workshop being shoved in my spam email for weeks). We started up a KotlinTown group in CodeTown for anyone interested in meeting other JUG members of all levels — introductory and experienced Java users alike!
One of the things I wanted to start doing with KotlinTown is Kotlin Tuesdays: we’re still figuring out a format — I’d like to get a chat successfully set up for Kotlin use only, but in the meantime, I’m posting both here and there and if you’re interested in joining the community, comment below or message us!
TornadoFX was created by Edvin Syse, who leveraged Kotlin to create a framework for JavaFX. TornadoFX was my first love with Kotlin, and I highly recommend checking out my other posts or even Github for other really cool projects people have done playing with TornadoFX. As a developer with extensive experience in large-scale web development, I definitely have JS fatigue with the amount of new frameworks that seem to come out every year. I’m not the only one either, Mike Hearn’s post on It’s Time to Kill the Web sounds more plausible by the day.
THAT ASIDES I am not here to argue web v. native development. TornadoFX is really fun and is a small framework (hence, short learning curve), and it’s a great way to start learning Kotlin without being bogged down by servers or mobile or web development. Ya’ll ready? Good. You’re not required to know JavaFX to get started with native application development, and I’ll be providing Kotlin and TornadoFX documentation below. Documentation for TornadoFX also has extensive examples with a convenient search function for you to browse around.
1. On IntelliJ, go to Preferences > Plugins and make sure Kotlin and TornadoFX is installed.
2. General consensus among TorandoFXers is that Maven is a lot easier to use — if you’re not familiar with Gradle v. Maven, don’t worry about it. It is not necessary to understand how to use either for this project, but don’t hesitate to ask for questions on setup if you run into a blocker. Once you have your plugins downloaded, open a new project and select the tornadofx-maven-plugin, which will mostly set up your project immediately.
3. It seems like the latest update for Kotlin didn’t pick up the latest running update for your Kotlin version (if you’re not sure what version you’re running, check your plugins for the latest i.e. 1.2.41). Go into your pom.xml file to edit the version and click “Import Changes” below. If it’s builds successfully, you’re good to go!
4. You’ll need to set up minimal configuration to run an instance of your application: luckily, the TornadoFX Maven plugin already did most of the painful setup for you. Go to Run > Edit Configurations, and add a new TornadoFX Configuration. Just name your config something meaningful and grab the MyApp class address, since that is already set up to launch the app. $MODULE_DIR$ detects the source of your project in your computer so you don’t have to dig for an address. Add the 1.8 JDK to the JRE and the name of your project to the classpath of the module, then hit Apply and OK. If you don’t have 1.8 JDK, you’ll need to download it from Oracle.
5. Press “Run” (or the green arrow) and you’ll see an instance of the skeleton application!Not too bad, right? The setup is always the worst part of a project.
While TornadoFX has a lot of great components made specifically for the framework like Datagrid or Datepicker, already pre-made for your ease of use, I want to create my own neighborhood with specific streets and houses and I want full control of what goes in each block, so I will use the JavaFX GridPane written with TornadoFX DSLs. You’ll notice that you already have an app and view folder in your project — you’ll want to add a controller and model folder as well for easy organization. If you’re not familiar with MVC, the structure is a general architectural pattern developers have come up with to work with large frameworks, all split to handle different aspects of the application.
What I had was house icons I downloaded from a free icon site — you’re welcome to use the ones in my project or find your own. Downloading free icons is great especially since I’m not a graphic designer myself. TornadoFX has all the features JavaFX has, if you want to know how to do something, look up Oracle JavaFX documentation/examples and transfer it to Kotlin! Here’s why I love Kotlin — in JavaFX, a gridpane is typically created like so:
class GridPaneExample {
/**
* @param args the command line arguments
*/
public static void main (String[ ] args) {
Application.launch(args);
}
@Override
public void start (Stage primaryStage) {
int numCol = 8;
GridPane pane = new GridPane( );
pane.setVgap(15);
pane.setPadding(new Insets(15, 15, 15, 15) );
// one row
for (int i = 0; i < numCol; i++) {
pane.add(horizontalStreetPane( ));
}
// show scene
Scene scene = new Scene(pane);
primaryStage.setScene(scene);
primaryStage.show( );
}
public StackPane horizontalStreetPane( ) {
Rectangle grass = new Rectangle(100.0, 100.0);
grass.setFill(Color.GREEN);
StackPane stack = new StackPane(grass);
Rectangle street = new Rectangle(100.0, 40.0);
street.setFill(Color.GRAY);
// adding additional elements overlays elements
stack.getChildren( ).add(street);
return stack;
}
A JavaFX rendering of GridPane with a single row
Given that I want to have crossing streets and houses placed in different parts of the grid, this code can grow redundant quickly. With TornadoFX and Kotlin, you can easily use DSLs to create an equivalent layout and easily add properties with apply to the parent. In Kotlin, you’ll notice that the code is a lot easier to read, and there are a couple syntactic things you see right away:
// TornadoFX has View, Controller, and Model components, allowing you to separate your code.
class MyApp: View( ) {
// global variables
val numCol : Int = 8;
// every view has a root component
override val root = gridpane {
vgap = 15.0
padding = insets( 15)
for (var i in 1..numCol) {
add(horitzontalStreetPane( ))
}
fun horizontalStreetPane( ): StackPane {
return stackpane {
rectangle {
fill = c("4E9830")
width = 100.0
height = 100.0
}
rectangle {
fill = c("919191")
width = 100.0
height = 40.0
}
}
}
A TornadoFX rendering of the above example.
That being said, I want to separate my individual components in the controller for function logic and create a quick and dirty version of my GridPane. You can test your results with the run every time. Below, are some more notible features for TornadoFX:
class NeighborhoodView: View( ) {
// dependency injection
val controller: NeighborhoodController by inject( )
// set up neighborhood
override val root = gridpane {
row {
add(controller.grassPane( ))
add(controller.grassPane( ))
add(controller.housePane(Pos.BASELINE_LEFT))
add(controller.verticalStreetPane( ))
add(controller.housePane(Pos.BASELINE_CENTER))
add(controller.verticalStreetPane( ))
add(controller.housePane(Pos.BASELINE_CENTER))
add(controller.grassPane( ))
}
// you can add a lot more rows below as you like.
// use this to check placement:
// isGridLinesVisible = true
}
}
class Styles: Stylesheet( ) {
companion object {
val main by cssclass( )
val neighborhood by cssclass( )
}
init {
main {
backgroundColor += c("222222")
}
neighborhood {
backgroundColor += c("4E9830")
prefWidth = 470.px
prefHeight = 590.px
}
}
}
}
// located in the controller package
class NeighborhoodController: Controller( ) {
fun housePane(position: Pos) : StackPane {
val houseNum = (1..6).random( )
return StackPane.apply {
rectangle {
fill = c("4E9830")
width = 100.0
height = 100.0
}
imageview("house$houseNum.png").apply {
alignment = position
}
}
fun grassPane( ) : StackPane {
return StackPane.apply {
rectangle {
fill = c("4E9830")
width = 100.0
height = 40.0
}
}
}
fun verticalStreetPane( ) : StackPane {
return StackPane.apply {
rectangle {
fill = c("4E9830")
width = 100.0
height = 100.0
}
rectangle {
fill = c("919191")
width = 100.0
height = 100.0
}
}
}
private fun ClosedRange.random( ) =Random( ).nextInt(endInclusive - start) + start
}
A TornadoFX rendering of the above example.
Build your neighborhood however you like:
One of the neat things about TornadoFX streamlines complex JavaFX layouts, including tabpane and tableview. The following is seriously crash course on some of these concepts, so I’m attaching documentation for the model creation and binding for the following result:
class MainView: View("Neighborhood Cat Scheduler" ) {
override val root = stackpane {
borderpane {
prefHeight = 700.0
prefWidth = 800.0
top(NeighborhoodView::class)
left(BottomView::class)
}
addClass(Styles.main)
}
}
// Kotlin data classes simplifies the way we hold data
data class CatSchedule(val ownerName: String? = null, val catName: String? = null,
val address: String? = null, val time: String? = null,val catImage: String? = null)
// allows for easy data binding for display and changes
class CatScheduleModel: ItemViewModel/span>CatSchedule>( ) {
val ownerName = bind { item?.ownerName?.toProperty( ) }
val catName = bind { item?.catName?.toProperty( ) }
val address = bind { item?.address?.toProperty( ) }
val time = bind { item?.time?.toProperty( ) }
val catImage = bind { item?.catImage?.toProperty( ) }
}
// modelview in displayed view
class BottomView: View( ) {
private val model: CatScheduleModel by inject( )
private val mondays = listOf(
CatSchedule("Tom Mariano", "Meowsers", "1110 6th Street", "3:00PM", "/kitty/kitty1.png"),
CatSchedule("John Hill", "Tom", "1115 6th Street", "4:00PM", "/kitty/kitty2.png"),
CatSchedule("Louise Vargas", "Mr. Whiskers", "1120 6th Street", "4:30PM", "/kitty/kitty3.png"),
CatSchedule("Dale Benton", "Pepper", "2111 8th Street", "7:00PM", "/kitty/kitty4.png"),
CatSchedule("Tucker Harrison", "Princess", "2267 8th Street", "8:00PM", "/kitty/kitty5.png")
).observable()
override val root = hbox {
tabpane {
tab("Monday") {
tableview(mondays) {
column("Owner", CatSchedule:ownerName)
column("Cat", CatSchedule:catName)
column("Address", CatSchedule:address).remainingWidth( )
column("Time", CatSchedule:time)
bindSelected(model)
smartResize( )
}
}
addClass(Styles.schedule)
}
stackpane {
rectangle {
width = 200.0
height = 200.0
fill = Color.TRANSPARENT
}
imageview(cat, true)
}
}
End result:
We have a lot of neat things going, but there’s a lot more we could do from here: for example, the cats avis might change per selection, and maybe we want to be able to edit our schedule. A friend of my recommended I make this canine-compatible as well: I’m inclined to agree since I’m more of a dog person myself. I’d like to be really ambitious and connect those actions with the houses as well — what are your ideas with the project?
I intend on setting up a chat on here - tried earlier last week but I'm hoping the endeavor will be successful later this week. Stay tuned!
Tags:
Sorry it took me a while - I promise I'll deliver the next part ON TIME y'all. Any questions?
Codetown is a social network. It's got blogs, forums, groups, personal pages and more! You might think of Codetown as a funky camper van with lots of compartments for your stuff and a great multimedia system, too! Best of all, Codetown has room for all of your friends.
Created by Michael Levin Dec 18, 2008 at 6:56pm. Last updated by Michael Levin May 4, 2018.
Check out the Codetown Jobs group.

Morgan Stanley engineers Jim Gough and Andreea Niculcea showed how they're retooling the bank's API program for AI agents using MCP and FINOS CALM. Live demos covered compliance guardrails, deployment gates, and zero-downtime rollouts across 100+ APIs. First API deployment shrank from two years to two weeks. They also demoed Google's A2A protocol running alongside MCP.
By Steef-Jan WiggersChristine Lemmer-Webber, Executive Director at the Spritely Institute, and David Thompson, CTO at the Spritely Institute, presented “Spritely: Infrastructure for the Future of the Internet” at QCon London 2026, where they discussed how Spritely works to decentralize the Internet with new foundational technologies that put users in control.
By Michael Redlich
You can find your way through an organization by figuring out what artifacts people leave behind, David Grizzanti mentioned at InfoQ Dev Summit Boston. He compared culture to anthropology, suggested studying behaviors, power dynamics, and decisions first, and then patiently model and reward new norms, build allies, and use influence and leading by example, to shift engineering culture over time.
By Ben Linders
At QCon London 2026, Jeff Smith discussed the growing mismatch between AI coding models and real-world software development. While AI tools are enabling developers to generate code faster than ever, Smith argued that the models themselves are increasingly “stale” because they lack the repository-specific knowledge required to produce production-ready contributions.
By Daniel Dominguez
Claude Opus 4.6 discovered 22 Firefox vulnerabilities in two weeks, including 14 high-severity bugs, as nearly 20% of all critical Firefox vulnerabilities were fixed in 2025. The AI also wrote working exploits for two bugs, demonstrating emerging capabilities that give defenders a temporary advantage but signal an accelerating arms race in cybersecurity.
By Steef-Jan Wiggers
© 2026 Created by Michael Levin.
Powered by