Mousedown events delayed or sluggish on mobile

I am learning about Elm and web apps by writing a little game called sootl.

It’s all SVGs animated with requestAnimationFrame(), and you control your player by clicking or touch the screen.

Everything seemed good on desktop browsers, but on mobile it took an age for the mousedown event to trigger when you touched.

Update: Thanks to @zatnosk on mastodon.social for letting me know there is a better way: 300ms tap delay, gone away. Short answer: add a <meta name="viewport" content="width=device-width"> tag inside your <head> tag.

It turns out that under certain circumstances, some mobile devices wait after the first touch happens to see whether you are going to swipe before they emit the mousedown event.

So, the short answer was to listen for touchstart events as well as mousedown ones. In Elm that looked like this change: e77e055e470c38489657ecaf5edc54a4e5f85782.

Simple example of Netty 4 usage

I feel the title of this post over-promises, since I was not able to make an example that seemed simple to me.

Anyway, here is a near-minimal example of how to use Netty to make a server that shouts back at you whatever you say:

NettyExample.java:

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.SocketChannel;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil;
import java.nio.charset.Charset;

class NettyExample
{
    public static void main( String[] args ) throws Exception
    {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try
        {
            new ServerBootstrap()
                .group( bossGroup, workerGroup )
                .channel( NioServerSocketChannel.class )
                .childHandler( new Init() )
                .bind( 1337 ).sync().channel().closeFuture().sync();
        }
        finally
        {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    private static class Init extends ChannelInitializer
    {
        @Override
        public void
        initChannel( SocketChannel ch ) throws Exception
        {
            ch.pipeline().addLast( new ShoutyHandler() );
        }
    }

    private static class ShoutyHandler extends ChannelInboundHandlerAdapter
    {
        @Override
        public void channelRead( ChannelHandlerContext ctx, Object msg )
        {
            try
            {
                Charset utf8 = CharsetUtil.UTF_8;
                String in = ( (ByteBuf)msg ).toString( utf8 );
                String out = in.toUpperCase(); // Shout!
                ctx.writeAndFlush( Unpooled.copiedBuffer( out, utf8 ) );
            }
            finally
            {
                ReferenceCountUtil.release( msg );
            }
        }

        @Override
        public void exceptionCaught(
            ChannelHandlerContext ctx, Throwable cause )
        {
            cause.printStackTrace();
            ctx.close();
        }
    }
}

The lines that actually do something useful are highlighted in red. If anyone knows how to make it shorter, please comment below. It seems a lot to me.

To run this, do:

sudo apt-get install openjdk-8-jdk
wget 'http://search.maven.org/remotecontent?filepath=io/netty/netty-all/4.1.5.Final/netty-all-4.1.5.Final.jar -O netty-all-4.1.5.Final.jar'
javac -Werror -cp netty-all-4.1.5.Final.jar:. NettyExample.java && java -cp netty-all-4.1.5.Final.jar:. NettyExample

Then in another terminal:

echo "Hello, world" | nc localhost 1337

and observe the response:

HELLO, WORLD

Comparison with Node.js

Just for comparison, here is an approximate equivalent in Node.js:

shouty.js:

var net = require('net');

var server = net.createServer(
    function( socket ) {
        socket.setEncoding('utf8');
        socket.on(
            'data',
            function( data ) {
                socket.end( data.toUpperCase() );
            }
        )
    }
);

server.listen( 1337, "localhost" );

To run it, do:

sudo apt-get install nodejs-legacy
node shouty.js

Then in another terminal:

echo "Hello, world" | nc localhost 1337

and observe the response:

HELLO, WORLD

Elm resizeable SVG canvas filling the screen

I am toying with writing an SVG-based game in (exciting-looking JavaScript-replacement) Elm, and I wanted an SVG that filled the whole screen and resized when the screen resized. I found it harder than I expected, so here is what I came up with for your information and comment.

Try the demo.

Because I was using Html.App.programWithFlags I was not able to shortcut the process and use just elm-reactor – I needed to create an HTML file and compile my code with elm-make.

index.html sets up a full-screen app and passes in the window size:

<!DOCTYPE HTML>
<html>
<head>
    <meta charset="UTF-8"/>
    <title>Sootl</title>
    <script src="sootl.js"></script>
    <style>
        html, body, svg
        {
            margin: 0px;
            padding: 0px;
            border: 0px;
            overflow: hidden;
        }
    </style>
</head>
<body>
</body>
<script>
var app = Elm.Main.fullscreen(
    {
        width:  window.innerWidth,
        height: window.innerHeight
    }
);
</script>
</html>

elm-package.json requires the Html, Svg and Window packages:

{
    "version": "1.0.0",
    "summary": "Stay out of the light!",
    "repository": "https://github.com/andybalaam/sootl.git",
    "license": "GPL2",
    "source-directories": [
        "."
    ],
    "exposed-modules": [],
    "dependencies": {
        "elm-lang/core": "4.0.5 <= v < 5.0.0",
        "elm-lang/html": "1.1.0 <= v < 2.0.0",
        "elm-lang/svg": "1.1.1 <= v < 2.0.0",
        "elm-lang/window": "1.0.0 <= v < 2.0.0"
    },
    "elm-version": "0.17.1 <= v < 0.18.0"
}

Main.elm contains the Elm code, which starts off with the window size from JavaScript, and then listens to resize events using the Window module.

import Html exposing (Html)
import Html.App exposing (programWithFlags)
import Svg exposing (..)
import Svg.Attributes exposing (..)
import Window


type alias Flags =
    { width : Int
    , height : Int
    }


type Msg = Resize Int Int


type alias Model =
    { screen :
        { width : Int
        , height : Int
        }
    }


init : Flags -> (Model, Cmd Msg)
init flags =
    (
        { screen =
            { width = flags.width
            , height = flags.height
            }
        }
    , Cmd.none
    )



view : Model -> Html Msg
view model =
    let
        sw = model.screen.width  - 0
        sh = model.screen.height - 0
    in
        svg
        [ width  <| toString sw
        , height <| toString sh
        ]
        [ rect
            [ x "0"
            , y "0"
            , width (toString model.screen.width)
            , height (toString model.screen.height)
            , fill "#eeffee"
            ]
            []
        , text'
            [ x <| toString <| sw / 2
            , y <| toString <| sh / 2
            , fontSize <| toString <| sh / 10
            , textAnchor "middle"
            ]
            [ text
                ((toString model.screen.width)
                ++ ", "
                ++ (toString model.screen.height))
            ]
        ]


update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
    let m =
        case msg of
            Resize w h -> {model | screen = {width = w, height = h}}
    in
        (m, Cmd.none)


subscriptions : Model -> Sub Msg
subscriptions model =
    Window.resizes (\size -> Resize size.width size.height)


main =
   programWithFlags
     { init = init
     , view = view
     , update = update
     , subscriptions = subscriptions
     }

I installed all the packages with:

elm-package install

Then I compiled the code with:

elm-make --output=sootl.js Main.elm

Now I launched elm-reactor:

elm-reactor

And navigated my browser to http://localhost:8000/index.html to see it working.

Elm makes me happy

Series: Snake in Elm, Elm makes me happy, Elm Basics, Elm Unit Test

Elm lets you write interactive web sites in a really clean and safe-feeling way, which is a contrast to the feeling I get when using JavaScript and HTML. It’s functional, but instead of that making everything seem hard, it makes me happy.

Slides: Elm makes me happy