Use polynomial regression to dynamically position a button

Added on 27/Jan/2019

The problem

I wanted to implement a design similar to the one below, where the button is always be positioned above the portal.

Fig.1 – The design to implement.
Fig.2 – Background.

My solution

I wanted to experiment with a bruteforce solution to position the button dynamically based on the screen size.

To achieve this I:

  1. Collected datapoints for the optimal position on different screen sizes.
  2. Then trained a model based on these datapoints to predict optimal button position.
  3. Finally positioned the button based on the returned coordinates when the page load.

Step 1: Collect datapoints

To collect the datapoints I added a new target at the center of the button on the background:

Background with target on button
Fig.3 – Background with target on button.

Then I added a JavaScript function to log the coordinates of my mouse & the screen size on each click:

var inputs = [];
var outputs = [];

document.onmousedown = function(e) {
  var x = e.pageX;
  var y = e.pageY;
  var h = screen.availHeight;
  var w = screen.availWidth;

  console.log('w:', w, 'h:', h, 'x:', x, 'y:', y);

  inputs.push({w: w, h: h});
  outputs.push({x: x, y: y});

Then I started clicking on the target while changing the screen size as shown in the video below:

Vid.1 – Video showing datapoints collection in action.

Step 2: Find optimal button position

Once I collected enough datapoints I converted them to a list of inputs (w, h) & outputs (x, y):

> 'X = [' +, i) => {return `(${e.w}, ${e.h})`;}).join(', ') + ']';
 X = [(2318, 1422), (2290, 1422), (2262, 1422), (2202, 1422), ...]

> 'Y = [' +, i) => {return `(${e.x}, ${e.y})`;}).join(', ') + ']';
 Y = [(416, 210), (413, 207), (414, 210), (413, 212), (414, 208), ...]

Then I used Gradient boosting technique with Multi Output Regressor from sklearn to train a regression model on these points:

from sklearn.ensemble import GradientBoostingRegressor
from sklearn.multioutput import MultiOutputRegressor

X = [...]
Y = [...]

model = MultiOutputRegressor(GradientBoostingRegressor(), n_jobs=-1), Y)

model.predict([[(2262, 1422)]])

Then I wrapped this model into a small Flask app that takes screen width & height as arguments and returns the optimal (x, y) coordinates for the button:

from flask import Flask, request, jsonify
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.multioutput import MultiOutputRegressor

app = Flask(__name__)

def my_route():
  w = request.args.get('w', default = 100, type = int)
  h = request.args.get('h', default = 100, type = int)

  X = [...]
  Y = [...]

  model = MultiOutputRegressor(GradientBoostingRegressor(), n_jobs=-1), Y)

  proposed_position = model.predict([[w, h]])

  return jsonify([proposed_position[0][0], proposed_position[0][1]])

Step 3: Position button on page load

Now that we have the model & api ready, we have to request the optimal button position from the api when loading the page and update the button position based on the coordinates that we get.

function position_apply_btn() {
  var h = window.innerHeight;
  var w = window.innerWidth;

 fetch('' + w + '&h=' + h)
 .then(response => {
     return response.json();
 .then(json => {
    let x = json[0];
    let y = json[1];

    set_position(x, y);

function set_position(x, y) {
  e = document.getElementById('action-btn'); = x - e.offsetWidth / 2 + 'px'; = y - e.offsetHeight / 2 + 'px';


Step 4: Result

Vid.2 – Video showing the end result.


I was surprised how well this turned out. I wonder if a similar but more optimized method (e.g. automatic screen resizes, optimal screen sizes …) can be used in a practical way to facilitate responsive web design?

Products You May Like

Articles You May Like

Africa’s Richest Man Aliko Dangote to Pocket $650M in Dividends
Instagram test visualizes hiding ‘Like’ counts from viewers
US halts recent practice of disclosing nuclear weapon total
Set Up a Calming Nook for Your Kid
There’s More to Beef Chuck Than Stews and Braises

Leave a Reply

Your email address will not be published. Required fields are marked *