TensorFlow.js: Machine Learning Diretamente no Browser Com JavaScript
Ola HaWkers, e se voce pudesse executar modelos de machine learning diretamente no navegador do usuario, sem precisar de servidor? Em 2025, isso nao e mais futuro - e realidade, e TensorFlow.js e a ferramenta que torna isso possivel.
Neste guia, vamos explorar como usar TensorFlow.js para criar aplicacoes inteligentes que rodam 100% no client-side.
Por Que Machine Learning no Browser
Executar ML no browser oferece vantagens unicas que muitos desenvolvedores ainda nao exploram.
Beneficios
Privacidade:
- Dados nunca saem do dispositivo do usuario
- Sem preocupacoes com LGPD/GDPR para processamento
- Ideal para aplicacoes sensiveis
Performance:
- Sem latencia de rede para inferencia
- Usa GPU do dispositivo via WebGL
- Respostas em tempo real
Custo:
- Zero custo de servidor para inferencia
- Escala ilimitada sem aumentar infraestrutura
- Democratiza acesso a ML
Experiencia:
- Funciona offline
- Interatividade em tempo real
- Nao depende de conexao
🧠 Contexto: TensorFlow.js e usado por milhoes de desenvolvedores e empresas como Google, Spotify e Airbnb para ML no browser.
Primeiros Passos Com TensorFlow.js
Vamos configurar um projeto e entender os conceitos basicos.
Instalacao
# Via NPM
npm install @tensorflow/tfjs
# Ou via CDN no HTML
# <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"></script>Conceitos Fundamentais
import * as tf from '@tensorflow/tfjs';
// Tensors: Arrays multidimensionais
const tensor1D = tf.tensor([1, 2, 3, 4]);
const tensor2D = tf.tensor([[1, 2], [3, 4]]);
// Shapes: Dimensoes do tensor
console.log(tensor2D.shape); // [2, 2]
// Operacoes basicas
const a = tf.tensor([1, 2, 3]);
const b = tf.tensor([4, 5, 6]);
const sum = a.add(b); // [5, 7, 9]
const product = a.mul(b); // [4, 10, 18]
const mean = a.mean(); // 2
// IMPORTANTE: Limpar memoria
tensor1D.dispose();
tensor2D.dispose();
// Ou usar tf.tidy() para limpeza automaticaGerenciamento de Memoria
// tf.tidy() limpa tensors intermediarios automaticamente
const result = tf.tidy(() => {
const a = tf.tensor([1, 2, 3]);
const b = tf.tensor([4, 5, 6]);
const c = a.add(b);
const d = c.mul(tf.scalar(2));
return d; // Apenas d e mantido
});
// Verificar uso de memoria
console.log(tf.memory());
// { numTensors: X, numDataBuffers: Y, numBytes: Z }
Usando Modelos Pre-Treinados
A forma mais rapida de comecar e usar modelos ja treinados.
Classificacao de Imagens Com MobileNet
import * as tf from '@tensorflow/tfjs';
import * as mobilenet from '@tensorflow-models/mobilenet';
async function classifyImage() {
// Carrega o modelo (primeira vez pode demorar)
const model = await mobilenet.load();
// Elemento de imagem do DOM
const img = document.getElementById('myImage');
// Classifica a imagem
const predictions = await model.classify(img);
console.log(predictions);
// [
// { className: 'golden retriever', probability: 0.89 },
// { className: 'Labrador retriever', probability: 0.08 },
// { className: 'cocker spaniel', probability: 0.02 }
// ]
}Deteccao de Objetos Com COCO-SSD
import * as cocoSsd from '@tensorflow-models/coco-ssd';
async function detectObjects() {
const model = await cocoSsd.load();
const img = document.getElementById('myImage');
const predictions = await model.detect(img);
predictions.forEach(prediction => {
console.log(prediction);
// {
// bbox: [x, y, width, height],
// class: 'person',
// score: 0.95
// }
});
// Desenhar bounding boxes
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
predictions.forEach(pred => {
ctx.strokeStyle = '#00FF00';
ctx.lineWidth = 2;
ctx.strokeRect(...pred.bbox);
ctx.fillStyle = '#00FF00';
ctx.fillText(
`${pred.class} (${Math.round(pred.score * 100)}%)`,
pred.bbox[0],
pred.bbox[1] - 5
);
});
}
Deteccao de Pose Com PoseNet/MoveNet
Um dos casos de uso mais populares e a deteccao de pose corporal.
Implementacao Basica
import * as poseDetection from '@tensorflow-models/pose-detection';
async function detectPose() {
// Configura o detector
const detectorConfig = {
modelType: poseDetection.movenet.modelType.SINGLEPOSE_LIGHTNING
};
const detector = await poseDetection.createDetector(
poseDetection.SupportedModels.MoveNet,
detectorConfig
);
// Usa video da webcam
const video = document.getElementById('webcam');
async function detect() {
const poses = await detector.estimatePoses(video);
if (poses.length > 0) {
const pose = poses[0];
// Keypoints do corpo
pose.keypoints.forEach(keypoint => {
if (keypoint.score > 0.5) {
console.log(`${keypoint.name}: (${keypoint.x}, ${keypoint.y})`);
// nose: (320, 180)
// left_eye: (310, 170)
// right_shoulder: (280, 250)
// etc.
}
});
}
requestAnimationFrame(detect);
}
detect();
}Aplicacao Pratica: Contador de Exercicios
class ExerciseCounter {
constructor() {
this.squatCount = 0;
this.isDown = false;
}
detectSquat(pose) {
const leftHip = pose.keypoints.find(k => k.name === 'left_hip');
const leftKnee = pose.keypoints.find(k => k.name === 'left_knee');
const leftAnkle = pose.keypoints.find(k => k.name === 'left_ankle');
if (!leftHip || !leftKnee || !leftAnkle) return;
// Calcula angulo do joelho
const angle = this.calculateAngle(
leftHip,
leftKnee,
leftAnkle
);
// Logica de contagem
if (angle < 90 && !this.isDown) {
this.isDown = true;
} else if (angle > 160 && this.isDown) {
this.isDown = false;
this.squatCount++;
this.onSquatComplete(this.squatCount);
}
}
calculateAngle(a, b, c) {
const radians = Math.atan2(c.y - b.y, c.x - b.x) -
Math.atan2(a.y - b.y, a.x - b.x);
let angle = Math.abs(radians * 180 / Math.PI);
if (angle > 180) angle = 360 - angle;
return angle;
}
onSquatComplete(count) {
console.log(`Agachamento #${count} completo!`);
}
}
Reconhecimento de Gestos de Mao
Outro caso de uso poderoso e detectar gestos das maos.
import * as handPoseDetection from '@tensorflow-models/hand-pose-detection';
async function detectHands() {
const model = handPoseDetection.SupportedModels.MediaPipeHands;
const detectorConfig = {
runtime: 'tfjs',
maxHands: 2
};
const detector = await handPoseDetection.createDetector(
model,
detectorConfig
);
const video = document.getElementById('webcam');
async function detect() {
const hands = await detector.estimateHands(video);
hands.forEach(hand => {
// 21 keypoints por mao
const landmarks = hand.keypoints;
// Detectar gesto de "joinha"
if (isThumbsUp(landmarks)) {
console.log('👍 Joinha detectado!');
}
// Detectar gesto de "paz"
if (isPeaceSign(landmarks)) {
console.log('✌️ Paz detectado!');
}
});
requestAnimationFrame(detect);
}
detect();
}
function isThumbsUp(landmarks) {
const thumbTip = landmarks[4];
const thumbBase = landmarks[2];
const indexTip = landmarks[8];
// Polegar para cima e outros dedos fechados
const thumbUp = thumbTip.y < thumbBase.y;
const indexDown = indexTip.y > landmarks[5].y;
return thumbUp && indexDown;
}
function isPeaceSign(landmarks) {
const indexTip = landmarks[8];
const middleTip = landmarks[12];
const ringTip = landmarks[16];
// Indicador e medio esticados, outros fechados
const indexUp = indexTip.y < landmarks[5].y;
const middleUp = middleTip.y < landmarks[9].y;
const ringDown = ringTip.y > landmarks[13].y;
return indexUp && middleUp && ringDown;
}
Classificacao de Texto Com Universal Sentence Encoder
TensorFlow.js nao e so para imagens - funciona otimo com texto.
import * as use from '@tensorflow-models/universal-sentence-encoder';
async function analyzeText() {
const model = await use.load();
// Embeddings de texto
const sentences = [
'Adorei este produto, muito bom!',
'Pessimo, nao recomendo.',
'O servico foi ok, nada excepcional.'
];
const embeddings = await model.embed(sentences);
console.log(embeddings.shape); // [3, 512]
// Classificacao de sentimento simples
const positiveRef = await model.embed(['Excelente, maravilhoso, perfeito']);
const negativeRef = await model.embed(['Terrivel, horrivel, pessimo']);
sentences.forEach(async (sentence, i) => {
const sentenceEmbed = embeddings.slice([i, 0], [1, 512]);
const positiveSim = cosineSimilarity(sentenceEmbed, positiveRef);
const negativeSim = cosineSimilarity(sentenceEmbed, negativeRef);
const sentiment = positiveSim > negativeSim ? 'Positivo' : 'Negativo';
console.log(`"${sentence}" -> ${sentiment}`);
});
}
function cosineSimilarity(a, b) {
const dotProduct = tf.sum(tf.mul(a, b));
const normA = tf.norm(a);
const normB = tf.norm(b);
return dotProduct.div(normA.mul(normB)).dataSync()[0];
}
Treinando Seu Proprio Modelo
Alem de usar modelos pre-treinados, voce pode criar os seus.
Modelo Simples de Classificacao
import * as tf from '@tensorflow/tfjs';
async function trainModel() {
// Dados de exemplo: XOR problem
const xs = tf.tensor2d([[0, 0], [0, 1], [1, 0], [1, 1]]);
const ys = tf.tensor2d([[0], [1], [1], [0]]);
// Arquitetura do modelo
const model = tf.sequential({
layers: [
tf.layers.dense({
inputShape: [2],
units: 8,
activation: 'relu'
}),
tf.layers.dense({
units: 4,
activation: 'relu'
}),
tf.layers.dense({
units: 1,
activation: 'sigmoid'
})
]
});
// Compilar
model.compile({
optimizer: tf.train.adam(0.1),
loss: 'binaryCrossentropy',
metrics: ['accuracy']
});
// Treinar
await model.fit(xs, ys, {
epochs: 100,
callbacks: {
onEpochEnd: (epoch, logs) => {
console.log(`Epoch ${epoch}: loss = ${logs.loss.toFixed(4)}`);
}
}
});
// Testar
const prediction = model.predict(tf.tensor2d([[0, 1]]));
prediction.print(); // ~0.95 (proximo de 1)
// Salvar modelo
await model.save('localstorage://meu-modelo');
}
// Carregar modelo salvo
async function loadModel() {
const model = await tf.loadLayersModel('localstorage://meu-modelo');
return model;
}Transfer Learning Com MobileNet
async function customImageClassifier() {
// Carrega MobileNet sem a camada final
const mobilenet = await tf.loadLayersModel(
'https://tfhub.dev/google/tfjs-model/mobilenet_v2_100_224/feature_vector/3/default/1',
{ fromTFHub: true }
);
// Congela pesos do MobileNet
mobilenet.trainable = false;
// Adiciona camadas customizadas
const model = tf.sequential();
model.add(mobilenet);
model.add(tf.layers.dense({
units: 128,
activation: 'relu'
}));
model.add(tf.layers.dropout({ rate: 0.5 }));
model.add(tf.layers.dense({
units: 3, // Numero de classes
activation: 'softmax'
}));
model.compile({
optimizer: tf.train.adam(0.0001),
loss: 'categoricalCrossentropy',
metrics: ['accuracy']
});
return model;
}
Otimizacao de Performance
Para ML no browser funcionar bem, otimizacao e crucial.
Backend WebGL vs CPU
// Verificar backend atual
console.log(tf.getBackend()); // 'webgl' ou 'cpu'
// Forcar WebGL (mais rapido)
await tf.setBackend('webgl');
// Ou usar WebGPU (mais rapido ainda, se disponivel)
if (navigator.gpu) {
await tf.setBackend('webgpu');
}Quantizacao de Modelos
// Ao converter modelo Python para TensorFlow.js
// Use quantizacao para reduzir tamanho
// tensorflowjs_converter \
// --input_format=tf_saved_model \
// --output_format=tfjs_graph_model \
// --quantize_float16=* \
// ./saved_model \
// ./tfjs_model
// Resultado: modelo 50% menor, performance similarWarm-up do Modelo
async function warmupModel(model) {
// Primeira inferencia e mais lenta
// Faca "warm-up" antes de uso real
const dummyInput = tf.zeros([1, 224, 224, 3]);
const warmupResult = model.predict(dummyInput);
warmupResult.dispose();
dummyInput.dispose();
console.log('Modelo aquecido e pronto!');
}
Aplicacao Completa: Classificador em Tempo Real
Vamos juntar tudo em uma aplicacao funcional.
import * as tf from '@tensorflow/tfjs';
import * as mobilenet from '@tensorflow-models/mobilenet';
class RealTimeClassifier {
constructor(videoElement, resultsElement) {
this.video = videoElement;
this.results = resultsElement;
this.model = null;
this.isRunning = false;
}
async init() {
// Configura WebGL
await tf.setBackend('webgl');
// Carrega modelo
console.log('Carregando modelo...');
this.model = await mobilenet.load({
version: 2,
alpha: 1.0
});
// Aquece modelo
await this.warmup();
// Inicia webcam
await this.setupCamera();
console.log('Pronto!');
}
async setupCamera() {
const stream = await navigator.mediaDevices.getUserMedia({
video: { facingMode: 'environment' }
});
this.video.srcObject = stream;
await new Promise(resolve => {
this.video.onloadedmetadata = resolve;
});
this.video.play();
}
async warmup() {
const dummyInput = tf.zeros([1, 224, 224, 3]);
const result = this.model.infer(dummyInput, 'conv_preds');
result.dispose();
dummyInput.dispose();
}
start() {
this.isRunning = true;
this.detect();
}
stop() {
this.isRunning = false;
}
async detect() {
if (!this.isRunning) return;
const predictions = await this.model.classify(this.video, 3);
this.displayResults(predictions);
requestAnimationFrame(() => this.detect());
}
displayResults(predictions) {
this.results.innerHTML = predictions
.map(p => `
<div class="prediction">
<span class="label">${p.className}</span>
<span class="score">${(p.probability * 100).toFixed(1)}%</span>
<div class="bar" style="width: ${p.probability * 100}%"></div>
</div>
`)
.join('');
}
}
// Uso
const classifier = new RealTimeClassifier(
document.getElementById('video'),
document.getElementById('results')
);
document.getElementById('startBtn').onclick = async () => {
await classifier.init();
classifier.start();
};Conclusao
TensorFlow.js democratiza o acesso a machine learning, permitindo que qualquer desenvolvedor JavaScript crie aplicacoes inteligentes sem precisar de infraestrutura de servidor ou conhecimento profundo de Python.
Em 2025, com GPUs mais poderosas nos dispositivos e modelos cada vez mais otimizados, o browser se torna uma plataforma legitima para ML - oferecendo privacidade, performance e escala sem custo de infraestrutura.
Se voce quer explorar outras formas de melhorar performance no browser, recomendo dar uma olhada no artigo WebAssembly e JavaScript: Performance Extrema onde exploramos como combinar essas tecnologias.

