I compiled and ran your exact code here (macOS 15.5, Terminal, kotlinc-jvm 2.3.0, JRE 25.0.1), and it displays the first* snake correctly. This shows that none of the Kotlin compiler, the Java or Kotlin libraries, the JVM, or the Terminal app are the problem here.
I’m guessing from the \s that you’re running on Windows. So it seems very possible there’s some Windows-specific problem, perhaps in the environment in which it’s launching the jar, or some interaction with your terminal program. (For example, what environment variables are set?)
It would also be worth checking the text encoding of your source file, to make sure your text editor has saved it in UTF-8 as expected. (For example, on macOS and other Unix-like OSs, you can run file emoji.kt, which here responds with emoji.kt: c program text, Unicode text, UTF-8 text.)
* However, the second snake appears as a single question mark. That’s because U+D83D is the high surrogate from a surrogate pair, and needs the corresponding low surrogate to be valid UTF-16 (as used by the JVM).
In this case, the snake character is U+1F40D, which is represented in UTF-16 by the surrogate pair U+D83D U+DC0D. And indeed if you change that line to use \uD83D\uDC0D, then it too shows the snake as expected.
What does chcp output? It probably is a code for an 8-bit codepage of Windows. Mine is 850.
Java always had limited support for the console. I haven’t tested in the last decade though. There is System.console(), but it is null a lot of the time, forcing your code to work with 2 different ways of working. A wrapper can easily solve this, but this wrapper will only be used by your code.
There is a library providing advanced console capabilities, but I can’t remember the name.
Why would you need special console support in this case? The program simply writes bytes to standard out; and as long as they’re using the encoding that the terminal program is expecting (which these days should default to UTF-8), it should be able to display them correctly.
My console is not UTF-8 by default. But I still use the old conhost.
Java will convert the output to the encoding of the Windows console you are using. If it can’t map a grapheme to the code page, it will output a question mark.
I do not know how the JavaScript environment handles the console. It probably forces UTF-8, and because the console is capable of outputting Unicode graphemes, it will show the emojis.