Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Tracking cursor position in real-time with remote monitoring (without JavaScript)
// Tracking cursor position in real-time without JavaScript
// Demo: https://twitter.com/davywtf/status/1124146339259002881
package main
import (
"fmt"
"net/http"
"strings"
)
const W = 50
const H = 50
var ch chan string
const head = `<head>
<style>
*{margin:0;padding:0}
html,body{width:100%;height:100%}
p{
width:10px;
height:10px;
display:inline-block;
border-right:1px solid #666;
border-bottom:1px solid #666
}
</style>
</head>`
func main() {
ch = make(chan string)
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
func handler(w http.ResponseWriter, r *http.Request) {
p := r.URL.Path
if p == "/" {
index(w, r)
return
} else if p == "/watch" {
watch(w, r)
return
} else {
if strings.HasPrefix(p, "/c") && strings.HasSuffix(p, ".png") {
ch <- p[1 : len(p)-4]
}
}
}
func index(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
flusher, ok := w.(http.Flusher)
if !ok {
return
}
w.Write([]byte(head))
flusher.Flush()
// Send <p> grid
w.Write([]byte("<body>\n"))
for i := 0; i < H; i++ {
w.Write([]byte("<div>"))
for j := 0; j < W; j++ {
w.Write([]byte(fmt.Sprintf("<p id=\"c%dx%d\"></p>", i, j)))
}
w.Write([]byte("</div>\n"))
}
w.Write([]byte("</body>\n"))
flusher.Flush()
// Send CSS
w.Write([]byte("<style>"))
for i := 0; i < H; i++ {
for j := 0; j < W; j++ {
id := fmt.Sprintf("c%dx%d", i, j)
s := fmt.Sprintf("#%s:hover{background:url(\"%s.png\")}", id, id)
w.Write([]byte(s))
}
}
w.Write([]byte("</style>"))
flusher.Flush()
}
func watch(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
flusher, ok := w.(http.Flusher)
if !ok {
return
}
w.Write([]byte(head))
flusher.Flush()
// Send <p> grid
w.Write([]byte("<body>\n"))
for i := 0; i < H; i++ {
w.Write([]byte("<div>"))
for j := 0; j < W; j++ {
w.Write([]byte(fmt.Sprintf("<p id=\"c%dx%d\"></p>", i, j)))
}
w.Write([]byte("</div>\n"))
}
w.Write([]byte("</body>\n"))
flusher.Flush()
// Listen to ch for updates and write to response
for p := range ch {
s := fmt.Sprintf("<style>#%s{background:#000}</style>\n", p)
_, err := w.Write([]byte(s))
if err != nil {
return
}
flusher.Flush()
}
}
@straube

This comment has been minimized.

Copy link

commented May 6, 2019

I was playing with this snippet and some extra CSS. It seems possible to add a grid covering an entire page.

To do that, it's required to wrap the tracking points in another element (a <div>, perhaps) and make it fixed positioned at 0 x 0. Then add pointer-events: none; to it, pointer-events: auto; to the p, and pointer-events: none; to p:hover.

Even it's not possible (I guess) to get the viewport size on the server side, one could stretch the grid to cover a page using CSS flexbox or grid layout. It's not a perfect solution but it could work.

Here is a summary of the styling I mentioned above:

.track-wrapper {
    position: fixed;
    top: 0; left: 0;
    pointer-events: none;
}

.p {
    pointer-events: auto;
}

.p:hover {
    pointer-events: none;
}

One known issue of this approach is some glitches when hovering a link, for example. The cursor will quickly toggle between pointer and arrow while the mouse is moving.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.