Calorie Count via Photo

Uploads your photo to ChatGPT's new vision model to automatically categorize the food and estimate the calories.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/** @jsxImportSource npm:hono@3/jsx */
import { fileToDataURL } from "https://esm.town/v/stevekrouse/fileToDataURL";
import { modifyImage } from "https://esm.town/v/stevekrouse/modifyImage";
import { chat } from "https://esm.town/v/stevekrouse/openai";
import { Hono } from "npm:hono@3";
function esmTown(url) {
return fetch(url, {
headers: {
"User-Agent":
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.142.86 Safari/537.36",
},
}).then(r => r.text());
}
const app = new Hono();
export default app.fetch;
app.get("/", async (c) =>
c.html(
<html>
<head>
<title>Calorie Estimator</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://cdn.tailwindcss.com" />
</head>
<body class="h-dvh bg-pink-100">
<div class="flex flex-col h-full items-center p-20">
<a href="/">
<h1 class="text-5xl font-extrabold text-pink-900 hover:underline">How many calories?</h1>
</a>
<div class="flex flex-col flex-grow justify-center items-center">
<form
action="/"
target="results"
class="flex flex-col"
method="post"
enctype="multipart/form-data"
>
<div></div>
<label
for="file"
id="upload"
class="px-6 py-4 m-4 text-3xl text-center cursor-pointer border-4 border-pink-400 rounded-xl transition-all bg-pink-300 hover:bg-pink-400 hover:text-pink-900 text-pink-900 shadow-2xl shadow-pink-500/50"
>
Upload photo
</label>
<input
class="hidden"
type="file"
id="file"
accept="image/*"
name="file"
/>
<img class="rounded-md shadow-2xl shadow-pink-500/50" id="preview" />
</form>
<iframe width="300px" height="600px" name="results" class="hidden" id="results" srcdoc="Loading...">
</iframe>
</div>
<a href="https://val.town/v/stevekrouse/calories" class="hover:underline text-pink-700">
view code
</a>
</div>
</body>
<script
dangerouslySetInnerHTML={{
__html: await esmTown("https://esm.town/v/stevekrouse/calories_script"),
}}
type="module"
/>
</html>,
));
app.post("/", async (c) => {
const formData = await c.req.formData();
const file = formData.get("file") as File;
console.log(file);
if (!file || !file.size) {
return c.text("No file uploaded", 400);
}
const estimates = await calorieEstimate(file);
if (estimates.error) {
return c.text(estimates.error, 500);
}
const dataURL = await fileToDataURL(file);
return c.html(
<div>
{estimates.map((estimate) => <p>{estimate.ingredient}: {estimate.calories} calories</p>)}
Total: {estimates.reduce((sum, estimate) => sum + estimate.calories, 0)}
</div>,
);
});
export async function calorieEstimate(file: File) {
const dataURL = await fileToDataURL(file);
try {
const response = await chat([
{
role: "system",
content: `You are an nutritionist.

The web game

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
/** @jsxImportSource npm:hono@3/jsx */
import { wordsMatching } from "https://esm.town/v/jdan/wordsMatching";
import { Hono } from "npm:hono@3";
function esmTown(url) {
return fetch(url, {
headers: {
"User-Agent":
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.142.86 Safari/537.36",
},
}).then(r => r.text());
}
const app = new Hono();
export default app.fetch;
app.get("/:letters", async (c) => {
const letters = c.req.param("letters");
const matchingWords = await wordsMatching(letters);
return c.html(
<html>
<head>
<title>Word game</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
{/* <script src="https://cdn.tailwindcss.com" /> */}
<style
dangerouslySetInnerHTML={{
__html: `
ul {
display: flex;
flex-flow: wrap column;
max-height: 150px; /* Limit height to whatever you need */
}`,
}}
/>
</head>
<body>
<div>
Find all the words using the letters: {letters.toUpperCase()}
</div>
<div id="state"></div>
<form id="form">
<input type="text" id="input" style={{ textTransform: "uppercase" }} />
<button type="submit">Submit</button>
</form>
<script
dangerouslySetInnerHTML={{
__html: `window.words = ${JSON.stringify(matchingWords)}`,
}}
type="module"
/>
<script
dangerouslySetInnerHTML={{
__html: await esmTown("https://esm.town/v/jdan/gameScript"),
}}
type="module"
/>
</body>
</html>,
);
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { fileToDataURL } from "https://esm.town/v/stevekrouse/fileToDataURL?v=1";
import { convertBlobToDataURL } from "https://esm.town/v/vladimyr/dataURL";
const file = new File(["testing stuff"], "test.txt", { type: "text/plain" });
const dataURL1 = await fileToDataURL(file);
const dataURL2 = await convertBlobToDataURL(file);
console.assert(dataURL1 === dataURL2);
console.log(dataURL1);
const resp = await fetch(dataURL1);
const text = await resp.text();
console.assert(resp.headers.get("content-type") === "text/plain");
console.assert(text === "testing stuff");
console.log(text);

Calorie Count via Photo

Uploads your photo to ChatGPT's new vision model to automatically categorize the food and estimate the calories.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/** @jsxImportSource npm:hono@3/jsx */
import { getGPT4oEmoji } from "https://esm.town/v/jdan/gpt4o_emoji";
import { blob } from "https://esm.town/v/std/blob?v=12";
import { fileToDataURL } from "https://esm.town/v/stevekrouse/fileToDataURL";
import { modifyImage } from "https://esm.town/v/stevekrouse/modifyImage";
import { chat } from "https://esm.town/v/stevekrouse/openai";
import { Hono } from "npm:hono@3";
function esmTown(url) {
return fetch(url, {
headers: {
"User-Agent":
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.142.86 Safari/537.36",
},
}).then(r => r.text());
}
const app = new Hono();
export default app.fetch;
app.get("/", async (c) =>
c.html(
<html>
<head>
<title>Calorie Estimator</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://cdn.tailwindcss.com" />
</head>
<body class="h-dvh bg-blue-100">
<div class="flex flex-col h-full items-center p-20">
<a href="/">
<h1 class="text-5xl font-extrabold text-blue-900 hover:underline">What emoji is this?</h1>
</a>
<div class="flex flex-col flex-grow justify-center items-center">
<form
action="/"
target="results"
class="flex flex-col"
method="post"
enctype="multipart/form-data"
>
<div></div>
<label
for="file"
id="upload"
class="px-6 py-4 m-4 text-3xl text-center cursor-pointer border-4 border-blue-400 rounded-xl transition-all bg-blue-300 hover:bg-blue-400 hover:text-blue-900 text-blue-900 shadow-2xl shadow-blue-500/50"
>
Upload photo
</label>
<input
class="hidden"
type="file"
id="file"
accept="image/*"
name="file"
/>
<img class="rounded-md shadow-2xl shadow-blue-500/50" id="preview" />
</form>
<iframe width="300px" height="600px" name="results" class="hidden" id="results" srcdoc="Loading...">
</iframe>
</div>
<a href="https://val.town/v/stevekrouse/calories" class="hover:underline text-blue-700">
view code
</a>
</div>
</body>
<script
dangerouslySetInnerHTML={{
__html: await esmTown("https://esm.town/v/stevekrouse/calories_script"),
}}
type="module"
/>
</html>,
));
app.post("/", async (c) => {
const formData = await c.req.formData();
const file = formData.get("file") as File;
console.log(file);
if (!file || !file.size) {
return c.text("No file uploaded", 400);
}
const key = `file-${file.type}-${Math.floor(Math.random() * 100000)}`;
const ident = await blob.set(key, file);
return c.html(
<div style={{ fontSize: 80 }}>
<a href={`/${encodeURIComponent(key)}`} target="results">
{key}
</a>
</div>,
);
});
/* on /:id, return the blob with the given key */
app.get("/:key", async (c) => {
const key = c.req.param("key");

Calorie Count via Photo

Uploads your photo to ChatGPT's new vision model to automatically categorize the food and estimate the calories.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
/** @jsxImportSource npm:hono@3/jsx */
import { getGPT4oEmoji } from "https://esm.town/v/jdan/gpt4o_emoji";
import { fileToDataURL } from "https://esm.town/v/stevekrouse/fileToDataURL";
import { modifyImage } from "https://esm.town/v/stevekrouse/modifyImage";
import { chat } from "https://esm.town/v/stevekrouse/openai";
import { Hono } from "npm:hono@3";
function esmTown(url) {
return fetch(url, {
headers: {
"User-Agent":
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.142.86 Safari/537.36",
},
}).then(r => r.text());
}
const app = new Hono();
export default app.fetch;
app.get("/", async (c) =>
c.html(
<html>
<head>
<title>Calorie Estimator</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://cdn.tailwindcss.com" />
</head>
<body class="h-dvh bg-blue-100">
<div class="flex flex-col h-full items-center p-20">
<a href="/">
<h1 class="text-5xl font-extrabold text-blue-900 hover:underline">What emoji is this?</h1>
</a>
<div class="flex flex-col flex-grow justify-center items-center">
<form
action="/"
target="results"
class="flex flex-col"
method="post"
enctype="multipart/form-data"
>
<div></div>
<label
for="file"
id="upload"
class="px-6 py-4 m-4 text-3xl text-center cursor-pointer border-4 border-blue-400 rounded-xl transition-all bg-blue-300 hover:bg-blue-400 hover:text-blue-900 text-blue-900 shadow-2xl shadow-blue-500/50"
>
Upload photo
</label>
<input
class="hidden"
type="file"
id="file"
accept="image/*"
name="file"
/>
<img class="rounded-md shadow-2xl shadow-blue-500/50" id="preview" />
</form>
<iframe width="300px" height="600px" name="results" class="hidden" id="results" srcdoc="Loading...">
</iframe>
</div>
<a href="https://val.town/v/stevekrouse/calories" class="hover:underline text-blue-700">
view code
</a>
</div>
</body>
<script
dangerouslySetInnerHTML={{
__html: await esmTown("https://esm.town/v/stevekrouse/calories_script"),
}}
type="module"
/>
</html>,
));
app.post("/", async (c) => {
const formData = await c.req.formData();
const file = formData.get("file") as File;
console.log(file);
if (!file || !file.size) {
return c.text("No file uploaded", 400);
}
const dataURL = await fileToDataURL(file);
const emoji = getGPT4oEmoji(dataURL);
return c.html(
<div style={{ fontSize: 80 }}>
Emoji: {emoji}
</div>,
);
});
1
Next